{
    "file_diffs": [
        {
            "old_file_content": "import abc\nimport asyncio\nimport binascii\nimport collections\nimport mimetypes\nimport cgi\nimport http.cookies\nimport io\nimport json\nimport re\nimport os\nimport warnings\n\nfrom urllib.parse import urlsplit, parse_qsl, urlencode, unquote\nfrom types import MappingProxyType\n\nfrom . import hdrs\nfrom .abc import AbstractRouter, AbstractMatchInfo\nfrom .helpers import reify\nfrom .log import web_logger\nfrom .multidict import (CIMultiDictProxy,\n                        CIMultiDict,\n                        MultiDictProxy,\n                        MultiDict)\nfrom .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11\nfrom .server import ServerHttpProtocol\nfrom .streams import EOF_MARKER\nfrom .websocket import do_handshake, MSG_BINARY, MSG_CLOSE, MSG_PING, MSG_TEXT\nfrom .errors import HttpProcessingError, WSClientDisconnectedError\n\n\n__all__ = [\n    'WSClientDisconnectedError',\n    'Application',\n    'HttpVersion',\n    'RequestHandler',\n    'RequestHandlerFactory',\n    'Request',\n    'StreamResponse',\n    'Response',\n    'WebSocketResponse',\n    'UrlDispatcher',\n    'UrlMappingMatchInfo',\n    'HTTPException',\n    'HTTPError',\n    'HTTPRedirection',\n    'HTTPSuccessful',\n    'HTTPOk',\n    'HTTPCreated',\n    'HTTPAccepted',\n    'HTTPNonAuthoritativeInformation',\n    'HTTPNoContent',\n    'HTTPResetContent',\n    'HTTPPartialContent',\n    'HTTPMultipleChoices',\n    'HTTPMovedPermanently',\n    'HTTPFound',\n    'HTTPSeeOther',\n    'HTTPNotModified',\n    'HTTPUseProxy',\n    'HTTPTemporaryRedirect',\n    'HTTPClientError',\n    'HTTPBadRequest',\n    'HTTPUnauthorized',\n    'HTTPPaymentRequired',\n    'HTTPForbidden',\n    'HTTPNotFound',\n    'HTTPMethodNotAllowed',\n    'HTTPNotAcceptable',\n    'HTTPProxyAuthenticationRequired',\n    'HTTPRequestTimeout',\n    'HTTPConflict',\n    'HTTPGone',\n    'HTTPLengthRequired',\n    'HTTPPreconditionFailed',\n    'HTTPRequestEntityTooLarge',\n    'HTTPRequestURITooLong',\n    'HTTPUnsupportedMediaType',\n    'HTTPRequestRangeNotSatisfiable',\n    'HTTPExpectationFailed',\n    'HTTPServerError',\n    'HTTPInternalServerError',\n    'HTTPNotImplemented',\n    'HTTPBadGateway',\n    'HTTPServiceUnavailable',\n    'HTTPGatewayTimeout',\n    'HTTPVersionNotSupported',\n]\n\n\nsentinel = object()\nretype = type(re.compile('@'))\n\n\nclass HeadersMixin:\n\n    _content_type = None\n    _content_dict = None\n    _stored_content_type = sentinel\n\n    def _parse_content_type(self, raw):\n        self._stored_content_type = raw\n        if raw is None:\n            # default value according to RFC 2616\n            self._content_type = 'application/octet-stream'\n            self._content_dict = {}\n        else:\n            self._content_type, self._content_dict = cgi.parse_header(raw)\n\n    @property\n    def content_type(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        \"\"\"The value of content part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get(_CONTENT_TYPE)\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_type\n\n    @property\n    def charset(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        \"\"\"The value of charset part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get(_CONTENT_TYPE)\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_dict.get('charset')\n\n    @property\n    def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):\n        \"\"\"The value of Content-Length HTTP header.\"\"\"\n        l = self.headers.get(_CONTENT_LENGTH)\n        if l is None:\n            return None\n        else:\n            return int(l)\n\n\nFileField = collections.namedtuple('Field', 'name filename file content_type')\n\n\n############################################################\n# HTTP Request\n############################################################\n\n\nclass Request(dict, HeadersMixin):\n\n    def __init__(self, app, message, payload, transport, reader, writer, *,\n                 _HOST=hdrs.HOST):\n        self._app = app\n        self._version = message.version\n        self._transport = transport\n        self._reader = reader\n        self._writer = writer\n        self._method = message.method\n        self._host = message.headers.get(_HOST)\n        self._path_qs = message.path\n        res = urlsplit(message.path)\n        self._path = unquote(res.path)\n        self._query_string = res.query\n        self._post = None\n        self._post_files_cache = None\n        self._headers = CIMultiDictProxy(message.headers)\n\n        if self._version < HttpVersion11:\n            self._keep_alive = False\n        elif message.should_close:\n            self._keep_alive = False\n        else:\n            self._keep_alive = True\n\n        # matchdict, route_name, handler\n        # or information about traversal lookup\n        self._match_info = None  # initialized after route resolving\n\n        self._payload = payload\n        self._cookies = None\n\n        self._read_bytes = None\n\n    @property\n    def method(self):\n        \"\"\"Read only property for getting HTTP method.\n\n        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.\n        \"\"\"\n        return self._method\n\n    @property\n    def version(self):\n        \"\"\"Read only property for getting HTTP version of request.\n\n        Returns aiohttp.protocol.HttpVersion instance.\n        \"\"\"\n        return self._version\n\n    @property\n    def host(self):\n        \"\"\"Read only property for getting *HOST* header of request.\n\n        Returns str or None if HTTP request has no HOST header.\n        \"\"\"\n        return self._host\n\n    @property\n    def path_qs(self):\n        \"\"\"The URL including PATH_INFO and the query string.\n\n        E.g, /app/blog?id=10\n        \"\"\"\n        return self._path_qs\n\n    @property\n    def path(self):\n        \"\"\"The URL including *PATH INFO* without the host or scheme.\n\n        E.g., ``/app/blog``\n        \"\"\"\n        return self._path\n\n    @property\n    def query_string(self):\n        \"\"\"The query string in the URL.\n\n        E.g., id=10\n        \"\"\"\n        return self._query_string\n\n    @reify\n    def GET(self):\n        \"\"\"A multidict with all the variables in the query string.\n\n        Lazy property.\n        \"\"\"\n        return MultiDictProxy(MultiDict(parse_qsl(self._query_string)))\n\n    @reify\n    def POST(self):\n        \"\"\"A multidict with all the variables in the POST parameters.\n\n        post() methods has to be called before using this attribute.\n        \"\"\"\n        if self._post is None:\n            raise RuntimeError(\"POST is not available before post()\")\n        return self._post\n\n    @property\n    def headers(self):\n        \"\"\"A case-insensitive multidict with all headers.\n\n        Lazy property.\n        \"\"\"\n        return self._headers\n\n    @property\n    def keep_alive(self):\n        \"\"\"Is keepalive enabled by client?\"\"\"\n        return self._keep_alive\n\n    @property\n    def match_info(self):\n        \"\"\"Result of route resolving.\"\"\"\n        return self._match_info\n\n    @property\n    def app(self):\n        \"\"\"Application instance.\"\"\"\n        return self._app\n\n    @property\n    def transport(self):\n        \"\"\"Transport used for request processing.\"\"\"\n        return self._transport\n\n    @property\n    def cookies(self):\n        \"\"\"Return request cookies.\n\n        A read-only dictionary-like object.\n        \"\"\"\n        if self._cookies is None:\n            raw = self.headers.get(hdrs.COOKIE, '')\n            parsed = http.cookies.SimpleCookie(raw)\n            self._cookies = MappingProxyType(\n                {key: val.value for key, val in parsed.items()})\n        return self._cookies\n\n    @property\n    def payload(self):\n        \"\"\"Return raw payload stream.\"\"\"\n        warnings.warn('use Request.content instead', DeprecationWarning)\n        return self._payload\n\n    @property\n    def content(self):\n        \"\"\"Return raw payload stream.\"\"\"\n        return self._payload\n\n    @asyncio.coroutine\n    def release(self):\n        \"\"\"Release request.\n\n        Eat unread part of HTTP BODY if present.\n        \"\"\"\n        chunk = yield from self._payload.readany()\n        while chunk is not EOF_MARKER or chunk:\n            chunk = yield from self._payload.readany()\n\n    @asyncio.coroutine\n    def read(self):\n        \"\"\"Read request body if present.\n\n        Returns bytes object with full request content.\n        \"\"\"\n        if self._read_bytes is None:\n            body = bytearray()\n            while True:\n                chunk = yield from self._payload.readany()\n                body.extend(chunk)\n                if chunk is EOF_MARKER:\n                    break\n            self._read_bytes = bytes(body)\n        return self._read_bytes\n\n    @asyncio.coroutine\n    def text(self):\n        \"\"\"Return BODY as text using encoding from .charset.\"\"\"\n        bytes_body = yield from self.read()\n        encoding = self.charset or 'utf-8'\n        return bytes_body.decode(encoding)\n\n    @asyncio.coroutine\n    def json(self, *, loader=json.loads):\n        \"\"\"Return BODY as JSON.\"\"\"\n        body = yield from self.text()\n        return loader(body)\n\n    @asyncio.coroutine\n    def post(self):\n        \"\"\"Return POST parameters.\"\"\"\n        if self._post is not None:\n            return self._post\n        if self.method not in ('POST', 'PUT', 'PATCH'):\n            self._post = MultiDictProxy(MultiDict())\n            return self._post\n\n        content_type = self.content_type\n        if (content_type not in ('',\n                                 'application/x-www-form-urlencoded',\n                                 'multipart/form-data')):\n            self._post = MultiDictProxy(MultiDict())\n            return self._post\n\n        body = yield from self.read()\n        content_charset = self.charset or 'utf-8'\n\n        environ = {'REQUEST_METHOD': self.method,\n                   'CONTENT_LENGTH': str(len(body)),\n                   'QUERY_STRING': '',\n                   'CONTENT_TYPE': self.headers.get(hdrs.CONTENT_TYPE)}\n\n        fs = cgi.FieldStorage(fp=io.BytesIO(body),\n                              environ=environ,\n                              keep_blank_values=True,\n                              encoding=content_charset)\n\n        supported_transfer_encoding = {\n            'base64': binascii.a2b_base64,\n            'quoted-printable': binascii.a2b_qp\n        }\n\n        out = MultiDict()\n        for field in fs.list or ():\n            transfer_encoding = field.headers.get(\n                'Content-Transfer-Encoding', None)\n            if field.filename:\n                ff = FileField(field.name,\n                               field.filename,\n                               field.file,  # N.B. file closed error\n                               field.type)\n                if self._post_files_cache is None:\n                    self._post_files_cache = {}\n                self._post_files_cache[field.name] = field\n                out.add(field.name, ff)\n            else:\n                value = field.value\n                if transfer_encoding in supported_transfer_encoding:\n                    # binascii accepts bytes\n                    value = value.encode('utf-8')\n                    value = supported_transfer_encoding[\n                        transfer_encoding](value)\n                out.add(field.name, value)\n\n        self._post = MultiDictProxy(out)\n        return self._post\n\n    def __repr__(self):\n        return \"<{} {} {} >\".format(self.__class__.__name__,\n                                    self.method, self.path)\n\n\n############################################################\n# HTTP Response classes\n############################################################\n\n\nclass StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._chunked = False\n        self._chunk_size = None\n        self._compression = False\n        self._compression_force = False\n        self._headers = CIMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add(hdrs.SET_COOKIE, value)\n\n    @property\n    def started(self):\n        return self._resp_impl is not None\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def chunked(self):\n        return self._chunked\n\n    @property\n    def compression(self):\n        return self._compression\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    def enable_chunked_encoding(self, chunk_size=None):\n        \"\"\"Enables automatic chunked transfer encoding.\"\"\"\n        self._chunked = True\n        self._chunk_size = chunk_size\n\n    def enable_compression(self, force=False):\n        \"\"\"Enables response compression with `deflate` encoding.\"\"\"\n        self._compression = True\n        self._compression_force = force\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path='/',\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path='/'):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers[hdrs.CONTENT_LENGTH] = str(value)\n        elif hdrs.CONTENT_LENGTH in self.headers:\n            del self.headers[hdrs.CONTENT_LENGTH]\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value).lower()\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers[CONTENT_TYPE] = ctype\n\n    def _start_pre_check(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            else:\n                return self._resp_impl\n        else:\n            return None\n\n    def start(self, request):\n        resp_impl = self._start_pre_check(request)\n        if resp_impl is not None:\n            return resp_impl\n\n        self._req = request\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        if self._compression:\n            if (self._compression_force or\n                    'deflate' in request.headers.get(\n                        hdrs.ACCEPT_ENCODING, '')):\n                resp_impl.add_compression_filter()\n\n        if self._chunked:\n            resp_impl.enable_chunked_encoding()\n            if self._chunk_size:\n                resp_impl.add_chunking_filter(self._chunk_size)\n\n        headers = self.headers.items()\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)' %\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def drain(self):\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n        yield from self._resp_impl.transport.drain()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True\n\n    def __repr__(self):\n        if self.started:\n            info = \"{} {} \".format(self._req.method, self._req.path)\n        else:\n            info = \"not started\"\n        return \"<{} {} {}>\".format(self.__class__.__name__,\n                                   self.reason, info)\n\n\nclass Response(StreamResponse):\n\n    def __init__(self, *, body=None, status=200,\n                 reason=None, headers=None,\n                 text=None, content_type=None):\n        super().__init__(status=status, reason=reason)\n\n        if headers is not None:\n            self.headers.extend(headers)\n\n        if body is not None and text is not None:\n            raise ValueError(\"body and text are not allowed together.\")\n\n        if text is not None:\n            if hdrs.CONTENT_TYPE not in self.headers:\n                # fast path for filling headers\n                if not isinstance(text, str):\n                    raise TypeError('text argument must be str (%r)' %\n                                    type(text))\n                if content_type is None:\n                    content_type = 'text/plain'\n                self.headers[hdrs.CONTENT_TYPE] = (\n                    content_type + '; charset=utf-8')\n                self._content_type = content_type\n                self._content_dict = {'charset': 'utf-8'}\n                self.body = text.encode('utf-8')\n            else:\n                self.text = text\n        else:\n            if content_type:\n                self.content_type = content_type\n            if body is not None:\n                self.body = body\n            else:\n                self.body = None\n\n    @property\n    def body(self):\n        return self._body\n\n    @body.setter\n    def body(self, body):\n        if body is not None and not isinstance(body, bytes):\n            raise TypeError('body argument must be bytes (%r)' % type(body))\n        self._body = body\n        if body is not None:\n            self.content_length = len(body)\n        else:\n            self.content_length = 0\n\n    @property\n    def text(self):\n        return self._body.decode(self.charset or 'utf-8')\n\n    @text.setter\n    def text(self, text):\n        if text is not None and not isinstance(text, str):\n            raise TypeError('text argument must be str (%r)' % type(text))\n\n        if self.content_type == 'application/octet-stream':\n            self.content_type = 'text/plain'\n        if self.charset is None:\n            self.charset = 'utf-8'\n\n        self.body = text.encode(self.charset)\n\n    @asyncio.coroutine\n    def write_eof(self):\n        body = self._body\n        if body is not None:\n            self.write(body)\n        yield from super().write_eof()\n\n\nclass WebSocketResponse(StreamResponse):\n\n    def __init__(self, *, protocols=()):\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._protocol = None\n        self._writer = None\n        self._reader = None\n        self._closing = False\n        self._loop = None\n        self._closing_fut = None\n\n    def start(self, request):\n        # make pre-check to don't hide it by do_handshake() exceptions\n        resp_impl = self._start_pre_check(request)\n        if resp_impl is not None:\n            return resp_impl\n\n        try:\n            status, headers, parser, writer, protocol = do_handshake(\n                request.method, request.headers, request.transport,\n                self._protocols)\n        except HttpProcessingError as err:\n            if err.code == 405:\n                raise HTTPMethodNotAllowed(request.method, ['GET'])\n            elif err.code == 400:\n                raise HTTPBadRequest(text=err.message, headers=err.headers)\n            else:  # pragma: no cover\n                raise HTTPInternalServerError() from err\n\n        if self.status != status:\n            self.set_status(status)\n        for k, v in headers:\n            self.headers[k] = v\n        self.force_close()\n\n        resp_impl = super().start(request)\n\n        self._reader = request._reader.set_parser(parser)\n        self._writer = writer\n        self._protocol = protocol\n        self._loop = request.app.loop\n        self._closing_fut = asyncio.Future(loop=self._loop)\n\n        return resp_impl\n\n    def can_start(self, request):\n        if self._writer is not None:\n            raise RuntimeError('Already started')\n        try:\n            _, _, _, _, protocol = do_handshake(\n                request.method, request.headers, request.transport,\n                self._protocols)\n        except HttpProcessingError:\n            return False, None\n        else:\n            return True, protocol\n\n    @property\n    def closing(self):\n        return self._closing\n\n    @property\n    def protocol(self):\n        return self._protocol\n\n    def ping(self, message='b'):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        self._writer.ping(message)\n\n    def pong(self, message='b'):\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        self._writer.pong(message)\n\n    def send_str(self, data):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        if not isinstance(data, str):\n            raise TypeError('data argument must be str (%r)' % type(data))\n        self._writer.send(data, binary=False)\n\n    def send_bytes(self, data):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)' %\n                            type(data))\n        self._writer.send(data, binary=True)\n\n    def close(self, *, code=1000, message=b''):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if not self._closing:\n            self._closing = True\n            self._writer.close(code, message)\n        else:\n            raise RuntimeError('Already closing')\n\n    @asyncio.coroutine\n    def wait_closed(self):\n        if self._closing_fut is None:\n            raise RuntimeError('Call .start() first')\n        yield from self._closing_fut\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self.wait_closed()\n        self._eof_sent = True\n\n    @asyncio.coroutine\n    def receive_msg(self):\n        if self._reader is None:\n            raise RuntimeError('Call .start() first')\n        while True:\n            try:\n                msg = yield from self._reader.read()\n            except Exception as exc:\n                self._closing_fut.set_exception(exc)\n                raise\n\n            if msg.tp == MSG_CLOSE:\n                if self._closing:\n                    exc = WSClientDisconnectedError(msg.data, msg.extra)\n                    self._closing_fut.set_exception(exc)\n                    raise exc\n                else:\n                    self._closing = True\n                    self._writer.close(msg.data, msg.extra)\n                    yield from self.drain()\n                    exc = WSClientDisconnectedError(msg.data, msg.extra)\n                    self._closing_fut.set_exception(exc)\n                    raise exc\n            elif not self._closing:\n                if msg.tp == MSG_PING:\n                    self._writer.pong(msg.data)\n                elif msg.tp in (MSG_TEXT, MSG_BINARY):\n                    return msg\n\n    @asyncio.coroutine\n    def receive_str(self):\n        msg = yield from self.receive_msg()\n        if msg.tp != MSG_TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not str\".format(msg.tp, msg.data))\n        return msg.data\n\n    @asyncio.coroutine\n    def receive_bytes(self):\n        msg = yield from self.receive_msg()\n        if msg.tp != MSG_BINARY:\n            raise TypeError(\n                \"Received message {}:{!r} is not bytes\".format(msg.tp,\n                                                               msg.data))\n        return msg.data\n\n    def write(self, data):\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n\n############################################################\n# HTTP Exceptions\n############################################################\n\nclass HTTPException(Response, Exception):\n\n    # You should set in subclasses:\n    # status = 200\n\n    status_code = None\n\n    def __init__(self, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        Response.__init__(self, status=self.status_code,\n                          headers=headers, reason=reason,\n                          body=body, text=text, content_type=content_type)\n        Exception.__init__(self, self.reason)\n        if self.body is None:\n            self.text = \"{}: {}\".format(self.status, self.reason)\n\n\nclass HTTPError(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 400s and 500s.\"\"\"\n\n\nclass HTTPRedirection(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 300s.\"\"\"\n\n\nclass HTTPSuccessful(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 200s.\"\"\"\n\n\nclass HTTPOk(HTTPSuccessful):\n    status_code = 200\n\n\nclass HTTPCreated(HTTPSuccessful):\n    status_code = 201\n\n\nclass HTTPAccepted(HTTPSuccessful):\n    status_code = 202\n\n\nclass HTTPNonAuthoritativeInformation(HTTPSuccessful):\n    status_code = 203\n\n\nclass HTTPNoContent(HTTPSuccessful):\n    status_code = 204\n\n\nclass HTTPResetContent(HTTPSuccessful):\n    status_code = 205\n\n\nclass HTTPPartialContent(HTTPSuccessful):\n    status_code = 206\n\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass _HTTPMove(HTTPRedirection):\n\n    def __init__(self, location, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Location'] = location\n        self.location = location\n\n\nclass HTTPMultipleChoices(_HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(_HTTPMove):\n    status_code = 301\n\n\nclass HTTPFound(_HTTPMove):\n    status_code = 302\n\n\n# This one is safe after a POST (the redirected location will be\n# retrieved with GET):\nclass HTTPSeeOther(_HTTPMove):\n    status_code = 303\n\n\nclass HTTPNotModified(HTTPRedirection):\n    # FIXME: this should include a date or etag header\n    status_code = 304\n\n\nclass HTTPUseProxy(_HTTPMove):\n    # Not a move, but looks a little like one\n    status_code = 305\n\n\nclass HTTPTemporaryRedirect(_HTTPMove):\n    status_code = 307\n\n\n############################################################\n# 4xx client error\n############################################################\n\n\nclass HTTPClientError(HTTPError):\n    pass\n\n\nclass HTTPBadRequest(HTTPClientError):\n    status_code = 400\n\n\nclass HTTPUnauthorized(HTTPClientError):\n    status_code = 401\n\n\nclass HTTPPaymentRequired(HTTPClientError):\n    status_code = 402\n\n\nclass HTTPForbidden(HTTPClientError):\n    status_code = 403\n\n\nclass HTTPNotFound(HTTPClientError):\n    status_code = 404\n\n\nclass HTTPMethodNotAllowed(HTTPClientError):\n    status_code = 405\n\n    def __init__(self, method, allowed_methods, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        allow = ','.join(sorted(allowed_methods))\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Allow'] = allow\n        self.allowed_methods = allowed_methods\n        self.method = method.upper()\n\n\nclass HTTPNotAcceptable(HTTPClientError):\n    status_code = 406\n\n\nclass HTTPProxyAuthenticationRequired(HTTPClientError):\n    status_code = 407\n\n\nclass HTTPRequestTimeout(HTTPClientError):\n    status_code = 408\n\n\nclass HTTPConflict(HTTPClientError):\n    status_code = 409\n\n\nclass HTTPGone(HTTPClientError):\n    status_code = 410\n\n\nclass HTTPLengthRequired(HTTPClientError):\n    status_code = 411\n\n\nclass HTTPPreconditionFailed(HTTPClientError):\n    status_code = 412\n\n\nclass HTTPRequestEntityTooLarge(HTTPClientError):\n    status_code = 413\n\n\nclass HTTPRequestURITooLong(HTTPClientError):\n    status_code = 414\n\n\nclass HTTPUnsupportedMediaType(HTTPClientError):\n    status_code = 415\n\n\nclass HTTPRequestRangeNotSatisfiable(HTTPClientError):\n    status_code = 416\n\n\nclass HTTPExpectationFailed(HTTPClientError):\n    status_code = 417\n\n\n############################################################\n# 5xx Server Error\n############################################################\n#  Response status codes beginning with the digit \"5\" indicate cases in\n#  which the server is aware that it has erred or is incapable of\n#  performing the request. Except when responding to a HEAD request, the\n#  server SHOULD include an entity containing an explanation of the error\n#  situation, and whether it is a temporary or permanent condition. User\n#  agents SHOULD display any included entity to the user. These response\n#  codes are applicable to any request method.\n\n\nclass HTTPServerError(HTTPError):\n    pass\n\n\nclass HTTPInternalServerError(HTTPServerError):\n    status_code = 500\n\n\nclass HTTPNotImplemented(HTTPServerError):\n    status_code = 501\n\n\nclass HTTPBadGateway(HTTPServerError):\n    status_code = 502\n\n\nclass HTTPServiceUnavailable(HTTPServerError):\n    status_code = 503\n\n\nclass HTTPGatewayTimeout(HTTPServerError):\n    status_code = 504\n\n\nclass HTTPVersionNotSupported(HTTPServerError):\n    status_code = 505\n\n\n############################################################\n# UrlDispatcher implementation\n############################################################\n\n\nclass UrlMappingMatchInfo(dict, AbstractMatchInfo):\n\n    def __init__(self, match_dict, route):\n        super().__init__(match_dict)\n        self._route = route\n\n    @property\n    def handler(self):\n        return self._route.handler\n\n    @property\n    def route(self):\n        return self._route\n\n    def __repr__(self):\n        return \"<MatchInfo {}: {}>\".format(super().__repr__(), self._route)\n\n\n@asyncio.coroutine\ndef defaultExpectHandler(request):\n    \"\"\"Default handler for Except: 100-continue\"\"\"\n    if request.version == HttpVersion11:\n        request.transport.write(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\")\n\n\nclass Route(metaclass=abc.ABCMeta):\n\n    def __init__(self, method, handler, name, expect_handler=None):\n        if expect_handler is None:\n            expect_handler = defaultExpectHandler\n        assert asyncio.iscoroutinefunction(expect_handler), \\\n            'Coroutine is expected, got {!r}'.format(expect_handler)\n\n        self._method = method\n        self._handler = handler\n        self._name = name\n        self._expect_handler = expect_handler\n\n    @property\n    def method(self):\n        return self._method\n\n    @property\n    def handler(self):\n        return self._handler\n\n    @property\n    def name(self):\n        return self._name\n\n    @abc.abstractmethod\n    def match(self, path):\n        \"\"\"Return dict with info for given path or\n        None if route cannot process path.\"\"\"\n\n    @abc.abstractmethod\n    def url(self, **kwargs):\n        \"\"\"Construct url for route with additional params.\"\"\"\n\n    @asyncio.coroutine\n    def handle_expect_header(self, request):\n        return (yield from self._expect_handler(request))\n\n    @staticmethod\n    def _append_query(url, query):\n        if query is not None:\n            return url + \"?\" + urlencode(query)\n        else:\n            return url\n\n\nclass PlainRoute(Route):\n\n    def __init__(self, method, handler, name, path, expect_handler=None):\n        super().__init__(method, handler, name, expect_handler=expect_handler)\n        self._path = path\n\n    def match(self, path):\n        # string comparison is about 10 times faster than regexp matching\n        if self._path == path:\n            return {}\n        else:\n            return None\n\n    def url(self, *, query=None):\n        return self._append_query(self._path, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<PlainRoute {name}[{method}] {path} -> {handler!r}\".format(\n            name=name, method=self.method, path=self._path,\n            handler=self.handler)\n\n\nclass DynamicRoute(Route):\n\n    def __init__(self, method, handler, name, pattern, formatter,\n                 expect_handler=None):\n        super().__init__(method, handler, name, expect_handler=expect_handler)\n        self._pattern = pattern\n        self._formatter = formatter\n\n    def match(self, path):\n        match = self._pattern.match(path)\n        if match is None:\n            return None\n        else:\n            return match.groupdict()\n\n    def url(self, *, parts, query=None):\n        url = self._formatter.format_map(parts)\n        return self._append_query(url, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return (\"<DynamicRoute {name}[{method}] {formatter} -> {handler!r}\"\n                .format(name=name, method=self.method,\n                        formatter=self._formatter, handler=self.handler))\n\n\nclass StaticRoute(Route):\n\n    limit = 8192\n\n    def __init__(self, name, prefix, directory, expect_handler=None):\n        assert prefix.startswith('/'), prefix\n        assert prefix.endswith('/'), prefix\n        super().__init__(\n            'GET', self.handle, name, expect_handler=expect_handler)\n        self._prefix = prefix\n        self._prefix_len = len(self._prefix)\n        self._directory = directory\n\n    def match(self, path):\n        if not path.startswith(self._prefix):\n            return None\n        return {'filename': path[self._prefix_len:]}\n\n    def url(self, *, filename, query=None):\n        while filename.startswith('/'):\n            filename = filename[1:]\n        url = self._prefix + filename\n        return self._append_query(url, query)\n\n    @asyncio.coroutine\n    def handle(self, request):\n        resp = StreamResponse()\n        filename = request.match_info['filename']\n        filepath = os.path.join(self._directory, filename)\n        if '..' in filename:\n            raise HTTPNotFound()\n        if not os.path.exists(filepath) or not os.path.isfile(filepath):\n            raise HTTPNotFound()\n\n        ct = mimetypes.guess_type(filename)[0]\n        if not ct:\n            ct = 'application/octet-stream'\n        resp.content_type = ct\n\n        file_size = os.stat(filepath).st_size\n        single_chunk = file_size < self.limit\n\n        if single_chunk:\n            resp.content_length = file_size\n        resp.start(request)\n\n        with open(filepath, 'rb') as f:\n            chunk = f.read(self.limit)\n            if single_chunk:\n                resp.write(chunk)\n            else:\n                while chunk:\n                    resp.write(chunk)\n                    chunk = f.read(self.limit)\n\n        return resp\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<StaticRoute {name}[{method}] {path} -> {directory!r}\".format(\n            name=name, method=self.method, path=self._prefix,\n            directory=self._directory)\n\n\nclass _NotFoundMatchInfo(UrlMappingMatchInfo):\n\n    def __init__(self):\n        super().__init__({}, None)\n\n    @property\n    def handler(self):\n        return self._not_found\n\n    @property\n    def route(self):\n        return None\n\n    @asyncio.coroutine\n    def _not_found(self, request):\n        raise HTTPNotFound()\n\n    def __repr__(self):\n        return \"<MatchInfo: not found>\"\n\n\nclass _MethodNotAllowedMatchInfo(UrlMappingMatchInfo):\n\n    def __init__(self, method, allowed_methods):\n        super().__init__({}, None)\n        self._method = method\n        self._allowed_methods = allowed_methods\n\n    @property\n    def handler(self):\n        return self._not_allowed\n\n    @property\n    def route(self):\n        return None\n\n    @asyncio.coroutine\n    def _not_allowed(self, request):\n        raise HTTPMethodNotAllowed(self._method, self._allowed_methods)\n\n    def __repr__(self):\n        return (\"<MatchInfo: method {} is not allowed (allowed methods: {}>\"\n                .format(self._method,\n                        ', '.join(sorted(self._allowed_methods))))\n\n\nclass UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n\n            self._register_endpoint(route)\n            return route\n\n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n        else:\n            pattern = '/' + '/'.join(parts)\n            formatter = '/' + '/'.join(format_parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n                formatter += '/'\n            try:\n                compiled = re.compile('^' + pattern + '$')\n            except re.error as exc:\n                raise ValueError(\n                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n            route = DynamicRoute(\n                method, handler, name, compiled,\n                formatter, expect_handler=expect_handler)\n\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route\n\n\n############################################################\n# Application implementation\n############################################################\n\n\nclass RequestHandler(ServerHttpProtocol):\n\n    def __init__(self, manager, app, router, **kwargs):\n        super().__init__(**kwargs)\n\n        self._manager = manager\n        self._app = app\n        self._router = router\n        self._middlewares = app.middlewares\n\n    def connection_made(self, transport):\n        super().connection_made(transport)\n\n        self._manager.connection_made(self, transport)\n\n    def connection_lost(self, exc):\n        self._manager.connection_lost(self, exc)\n\n        super().connection_lost(exc)\n\n    @asyncio.coroutine\n    def handle_request(self, message, payload):\n        now = self._loop.time()\n\n        app = self._app\n        request = Request(app, message, payload,\n                          self.transport, self.reader, self.writer)\n        try:\n            match_info = yield from self._router.resolve(request)\n\n            assert isinstance(match_info, AbstractMatchInfo), match_info\n\n            resp = None\n            request._match_info = match_info\n            if request.headers.get(hdrs.EXPECT, '').lower() == \"100-continue\":\n                resp = (\n                    yield from match_info.route.handle_expect_header(request))\n\n            if resp is None:\n                handler = match_info.handler\n                for factory in reversed(self._middlewares):\n                    handler = yield from factory(app, handler)\n                resp = yield from handler(request)\n\n            if not isinstance(resp, StreamResponse):\n                raise RuntimeError(\n                    (\"Handler {!r} should return response instance, \"\n                     \"got {!r} [middlewares {!r}]\").format(\n                         match_info.handler,\n                         type(resp),\n                         self._middlewares))\n        except HTTPException as exc:\n            resp = exc\n\n        resp_msg = resp.start(request)\n        yield from resp.write_eof()\n\n        # notify server about keep-alive\n        self.keep_alive(resp_msg.keep_alive())\n\n        # log access\n        self.log_access(message, None, resp_msg, self._loop.time() - now)\n\n\nclass RequestHandlerFactory:\n\n    def __init__(self, app, router, *,\n                 handler=RequestHandler, loop=None, **kwargs):\n        self._app = app\n        self._router = router\n        self._handler = handler\n        self._loop = loop\n        self._connections = {}\n        self._kwargs = kwargs\n        self._kwargs.setdefault('logger', app.logger)\n\n    @property\n    def connections(self):\n        return list(self._connections.keys())\n\n    def connection_made(self, handler, transport):\n        self._connections[handler] = transport\n\n    def connection_lost(self, handler, exc=None):\n        if handler in self._connections:\n            del self._connections[handler]\n\n    @asyncio.coroutine\n    def finish_connections(self, timeout=None):\n        for handler in self._connections.keys():\n            handler.closing()\n\n        @asyncio.coroutine\n        def cleanup():\n            sleep = 0.05\n            while self._connections:\n                yield from asyncio.sleep(sleep, loop=self._loop)\n                if sleep < 5:\n                    sleep = sleep * 2\n\n        if timeout:\n            try:\n                yield from asyncio.wait_for(\n                    cleanup(), timeout, loop=self._loop)\n            except asyncio.TimeoutError:\n                self._app.logger.warning(\n                    \"Not all connections are closed (pending: %d)\",\n                    len(self._connections))\n\n        for transport in self._connections.values():\n            transport.close()\n\n        self._connections.clear()\n\n    def __call__(self):\n        return self._handler(\n            self, self._app, self._router, loop=self._loop, **self._kwargs)\n\n\nclass Application(dict):\n\n    def __init__(self, *, logger=web_logger, loop=None,\n                 router=None, handler_factory=RequestHandlerFactory,\n                 middlewares=()):\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if router is None:\n            router = UrlDispatcher()\n        assert isinstance(router, AbstractRouter), router\n\n        self._router = router\n        self._handler_factory = handler_factory\n        self._finish_callbacks = []\n        self._loop = loop\n        self.logger = logger\n\n        for factory in middlewares:\n            assert asyncio.iscoroutinefunction(factory), factory\n        self._middlewares = tuple(middlewares)\n\n    @property\n    def router(self):\n        return self._router\n\n    @property\n    def loop(self):\n        return self._loop\n\n    @property\n    def middlewares(self):\n        return self._middlewares\n\n    def make_handler(self, **kwargs):\n        return self._handler_factory(\n            self, self.router, loop=self.loop, **kwargs)\n\n    @asyncio.coroutine\n    def finish(self):\n        callbacks = self._finish_callbacks\n        self._finish_callbacks = []\n\n        for (cb, args, kwargs) in callbacks:\n            try:\n                res = cb(self, *args, **kwargs)\n                if (asyncio.iscoroutine(res) or\n                        isinstance(res, asyncio.Future)):\n                    yield from res\n            except Exception as exc:\n                self._loop.call_exception_handler({\n                    'message': \"Error in finish callback\",\n                    'exception': exc,\n                    'application': self,\n                })\n\n    def register_on_finish(self, func, *args, **kwargs):\n        self._finish_callbacks.insert(0, (func, args, kwargs))\n\n    def __call__(self):\n        \"\"\"gunicorn compatibility\"\"\"\n        return self\n\n    def __repr__(self):\n        return \"<Application>\"\n",
            "new_file_content": "import abc\nimport asyncio\nimport binascii\nimport collections\nimport mimetypes\nimport cgi\nimport http.cookies\nimport io\nimport json\nimport re\nimport os\nimport warnings\n\nfrom urllib.parse import urlsplit, parse_qsl, urlencode, unquote\nfrom types import MappingProxyType\n\nfrom . import hdrs\nfrom .abc import AbstractRouter, AbstractMatchInfo\nfrom .helpers import reify\nfrom .log import web_logger\nfrom .multidict import (CIMultiDictProxy,\n                        CIMultiDict,\n                        MultiDictProxy,\n                        MultiDict)\nfrom .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11\nfrom .server import ServerHttpProtocol\nfrom .streams import EOF_MARKER\nfrom .websocket import do_handshake, MSG_BINARY, MSG_CLOSE, MSG_PING, MSG_TEXT\nfrom .errors import HttpProcessingError, WSClientDisconnectedError\n\n\n__all__ = [\n    'WSClientDisconnectedError',\n    'Application',\n    'HttpVersion',\n    'RequestHandler',\n    'RequestHandlerFactory',\n    'Request',\n    'StreamResponse',\n    'Response',\n    'WebSocketResponse',\n    'UrlDispatcher',\n    'UrlMappingMatchInfo',\n    'HTTPException',\n    'HTTPError',\n    'HTTPRedirection',\n    'HTTPSuccessful',\n    'HTTPOk',\n    'HTTPCreated',\n    'HTTPAccepted',\n    'HTTPNonAuthoritativeInformation',\n    'HTTPNoContent',\n    'HTTPResetContent',\n    'HTTPPartialContent',\n    'HTTPMultipleChoices',\n    'HTTPMovedPermanently',\n    'HTTPFound',\n    'HTTPSeeOther',\n    'HTTPNotModified',\n    'HTTPUseProxy',\n    'HTTPTemporaryRedirect',\n    'HTTPClientError',\n    'HTTPBadRequest',\n    'HTTPUnauthorized',\n    'HTTPPaymentRequired',\n    'HTTPForbidden',\n    'HTTPNotFound',\n    'HTTPMethodNotAllowed',\n    'HTTPNotAcceptable',\n    'HTTPProxyAuthenticationRequired',\n    'HTTPRequestTimeout',\n    'HTTPConflict',\n    'HTTPGone',\n    'HTTPLengthRequired',\n    'HTTPPreconditionFailed',\n    'HTTPRequestEntityTooLarge',\n    'HTTPRequestURITooLong',\n    'HTTPUnsupportedMediaType',\n    'HTTPRequestRangeNotSatisfiable',\n    'HTTPExpectationFailed',\n    'HTTPServerError',\n    'HTTPInternalServerError',\n    'HTTPNotImplemented',\n    'HTTPBadGateway',\n    'HTTPServiceUnavailable',\n    'HTTPGatewayTimeout',\n    'HTTPVersionNotSupported',\n]\n\n\nsentinel = object()\nretype = type(re.compile('@'))\n\n\nclass HeadersMixin:\n\n    _content_type = None\n    _content_dict = None\n    _stored_content_type = sentinel\n\n    def _parse_content_type(self, raw):\n        self._stored_content_type = raw\n        if raw is None:\n            # default value according to RFC 2616\n            self._content_type = 'application/octet-stream'\n            self._content_dict = {}\n        else:\n            self._content_type, self._content_dict = cgi.parse_header(raw)\n\n    @property\n    def content_type(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        \"\"\"The value of content part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get(_CONTENT_TYPE)\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_type\n\n    @property\n    def charset(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        \"\"\"The value of charset part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get(_CONTENT_TYPE)\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_dict.get('charset')\n\n    @property\n    def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):\n        \"\"\"The value of Content-Length HTTP header.\"\"\"\n        l = self.headers.get(_CONTENT_LENGTH)\n        if l is None:\n            return None\n        else:\n            return int(l)\n\n\nFileField = collections.namedtuple('Field', 'name filename file content_type')\n\n\n############################################################\n# HTTP Request\n############################################################\n\n\nclass Request(dict, HeadersMixin):\n\n    def __init__(self, app, message, payload, transport, reader, writer, *,\n                 _HOST=hdrs.HOST):\n        self._app = app\n        self._version = message.version\n        self._transport = transport\n        self._reader = reader\n        self._writer = writer\n        self._method = message.method\n        self._host = message.headers.get(_HOST)\n        self._path_qs = message.path\n        res = urlsplit(message.path)\n        self._path = unquote(res.path)\n        self._query_string = res.query\n        self._post = None\n        self._post_files_cache = None\n        self._headers = CIMultiDictProxy(message.headers)\n\n        if self._version < HttpVersion11:\n            self._keep_alive = False\n        elif message.should_close:\n            self._keep_alive = False\n        else:\n            self._keep_alive = True\n\n        # matchdict, route_name, handler\n        # or information about traversal lookup\n        self._match_info = None  # initialized after route resolving\n\n        self._payload = payload\n        self._cookies = None\n\n        self._read_bytes = None\n\n    @property\n    def method(self):\n        \"\"\"Read only property for getting HTTP method.\n\n        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.\n        \"\"\"\n        return self._method\n\n    @property\n    def version(self):\n        \"\"\"Read only property for getting HTTP version of request.\n\n        Returns aiohttp.protocol.HttpVersion instance.\n        \"\"\"\n        return self._version\n\n    @property\n    def host(self):\n        \"\"\"Read only property for getting *HOST* header of request.\n\n        Returns str or None if HTTP request has no HOST header.\n        \"\"\"\n        return self._host\n\n    @property\n    def path_qs(self):\n        \"\"\"The URL including PATH_INFO and the query string.\n\n        E.g, /app/blog?id=10\n        \"\"\"\n        return self._path_qs\n\n    @property\n    def path(self):\n        \"\"\"The URL including *PATH INFO* without the host or scheme.\n\n        E.g., ``/app/blog``\n        \"\"\"\n        return self._path\n\n    @property\n    def query_string(self):\n        \"\"\"The query string in the URL.\n\n        E.g., id=10\n        \"\"\"\n        return self._query_string\n\n    @reify\n    def GET(self):\n        \"\"\"A multidict with all the variables in the query string.\n\n        Lazy property.\n        \"\"\"\n        return MultiDictProxy(MultiDict(parse_qsl(self._query_string)))\n\n    @reify\n    def POST(self):\n        \"\"\"A multidict with all the variables in the POST parameters.\n\n        post() methods has to be called before using this attribute.\n        \"\"\"\n        if self._post is None:\n            raise RuntimeError(\"POST is not available before post()\")\n        return self._post\n\n    @property\n    def headers(self):\n        \"\"\"A case-insensitive multidict with all headers.\n\n        Lazy property.\n        \"\"\"\n        return self._headers\n\n    @property\n    def keep_alive(self):\n        \"\"\"Is keepalive enabled by client?\"\"\"\n        return self._keep_alive\n\n    @property\n    def match_info(self):\n        \"\"\"Result of route resolving.\"\"\"\n        return self._match_info\n\n    @property\n    def app(self):\n        \"\"\"Application instance.\"\"\"\n        return self._app\n\n    @property\n    def transport(self):\n        \"\"\"Transport used for request processing.\"\"\"\n        return self._transport\n\n    @property\n    def cookies(self):\n        \"\"\"Return request cookies.\n\n        A read-only dictionary-like object.\n        \"\"\"\n        if self._cookies is None:\n            raw = self.headers.get(hdrs.COOKIE, '')\n            parsed = http.cookies.SimpleCookie(raw)\n            self._cookies = MappingProxyType(\n                {key: val.value for key, val in parsed.items()})\n        return self._cookies\n\n    @property\n    def payload(self):\n        \"\"\"Return raw payload stream.\"\"\"\n        warnings.warn('use Request.content instead', DeprecationWarning)\n        return self._payload\n\n    @property\n    def content(self):\n        \"\"\"Return raw payload stream.\"\"\"\n        return self._payload\n\n    @asyncio.coroutine\n    def release(self):\n        \"\"\"Release request.\n\n        Eat unread part of HTTP BODY if present.\n        \"\"\"\n        chunk = yield from self._payload.readany()\n        while chunk is not EOF_MARKER or chunk:\n            chunk = yield from self._payload.readany()\n\n    @asyncio.coroutine\n    def read(self):\n        \"\"\"Read request body if present.\n\n        Returns bytes object with full request content.\n        \"\"\"\n        if self._read_bytes is None:\n            body = bytearray()\n            while True:\n                chunk = yield from self._payload.readany()\n                body.extend(chunk)\n                if chunk is EOF_MARKER:\n                    break\n            self._read_bytes = bytes(body)\n        return self._read_bytes\n\n    @asyncio.coroutine\n    def text(self):\n        \"\"\"Return BODY as text using encoding from .charset.\"\"\"\n        bytes_body = yield from self.read()\n        encoding = self.charset or 'utf-8'\n        return bytes_body.decode(encoding)\n\n    @asyncio.coroutine\n    def json(self, *, loader=json.loads):\n        \"\"\"Return BODY as JSON.\"\"\"\n        body = yield from self.text()\n        return loader(body)\n\n    @asyncio.coroutine\n    def post(self):\n        \"\"\"Return POST parameters.\"\"\"\n        if self._post is not None:\n            return self._post\n        if self.method not in ('POST', 'PUT', 'PATCH'):\n            self._post = MultiDictProxy(MultiDict())\n            return self._post\n\n        content_type = self.content_type\n        if (content_type not in ('',\n                                 'application/x-www-form-urlencoded',\n                                 'multipart/form-data')):\n            self._post = MultiDictProxy(MultiDict())\n            return self._post\n\n        body = yield from self.read()\n        content_charset = self.charset or 'utf-8'\n\n        environ = {'REQUEST_METHOD': self.method,\n                   'CONTENT_LENGTH': str(len(body)),\n                   'QUERY_STRING': '',\n                   'CONTENT_TYPE': self.headers.get(hdrs.CONTENT_TYPE)}\n\n        fs = cgi.FieldStorage(fp=io.BytesIO(body),\n                              environ=environ,\n                              keep_blank_values=True,\n                              encoding=content_charset)\n\n        supported_transfer_encoding = {\n            'base64': binascii.a2b_base64,\n            'quoted-printable': binascii.a2b_qp\n        }\n\n        out = MultiDict()\n        for field in fs.list or ():\n            transfer_encoding = field.headers.get(\n                'Content-Transfer-Encoding', None)\n            if field.filename:\n                ff = FileField(field.name,\n                               field.filename,\n                               field.file,  # N.B. file closed error\n                               field.type)\n                if self._post_files_cache is None:\n                    self._post_files_cache = {}\n                self._post_files_cache[field.name] = field\n                out.add(field.name, ff)\n            else:\n                value = field.value\n                if transfer_encoding in supported_transfer_encoding:\n                    # binascii accepts bytes\n                    value = value.encode('utf-8')\n                    value = supported_transfer_encoding[\n                        transfer_encoding](value)\n                out.add(field.name, value)\n\n        self._post = MultiDictProxy(out)\n        return self._post\n\n    def __repr__(self):\n        return \"<{} {} {} >\".format(self.__class__.__name__,\n                                    self.method, self.path)\n\n\n############################################################\n# HTTP Response classes\n############################################################\n\n\nclass StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._chunked = False\n        self._chunk_size = None\n        self._compression = False\n        self._compression_force = False\n        self._headers = CIMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add(hdrs.SET_COOKIE, value)\n\n    @property\n    def started(self):\n        return self._resp_impl is not None\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def chunked(self):\n        return self._chunked\n\n    @property\n    def compression(self):\n        return self._compression\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    def enable_chunked_encoding(self, chunk_size=None):\n        \"\"\"Enables automatic chunked transfer encoding.\"\"\"\n        self._chunked = True\n        self._chunk_size = chunk_size\n\n    def enable_compression(self, force=False):\n        \"\"\"Enables response compression with `deflate` encoding.\"\"\"\n        self._compression = True\n        self._compression_force = force\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path='/',\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path='/'):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers[hdrs.CONTENT_LENGTH] = str(value)\n        elif hdrs.CONTENT_LENGTH in self.headers:\n            del self.headers[hdrs.CONTENT_LENGTH]\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value).lower()\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers[CONTENT_TYPE] = ctype\n\n    def _start_pre_check(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            else:\n                return self._resp_impl\n        else:\n            return None\n\n    def start(self, request):\n        resp_impl = self._start_pre_check(request)\n        if resp_impl is not None:\n            return resp_impl\n\n        self._req = request\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        if self._compression:\n            if (self._compression_force or\n                    'deflate' in request.headers.get(\n                        hdrs.ACCEPT_ENCODING, '')):\n                resp_impl.add_compression_filter()\n\n        if self._chunked:\n            resp_impl.enable_chunked_encoding()\n            if self._chunk_size:\n                resp_impl.add_chunking_filter(self._chunk_size)\n\n        headers = self.headers.items()\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)' %\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def drain(self):\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n        yield from self._resp_impl.transport.drain()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True\n\n    def __repr__(self):\n        if self.started:\n            info = \"{} {} \".format(self._req.method, self._req.path)\n        else:\n            info = \"not started\"\n        return \"<{} {} {}>\".format(self.__class__.__name__,\n                                   self.reason, info)\n\n\nclass Response(StreamResponse):\n\n    def __init__(self, *, body=None, status=200,\n                 reason=None, headers=None,\n                 text=None, content_type=None):\n        super().__init__(status=status, reason=reason)\n\n        if headers is not None:\n            self.headers.extend(headers)\n\n        if body is not None and text is not None:\n            raise ValueError(\"body and text are not allowed together.\")\n\n        if text is not None:\n            if hdrs.CONTENT_TYPE not in self.headers:\n                # fast path for filling headers\n                if not isinstance(text, str):\n                    raise TypeError('text argument must be str (%r)' %\n                                    type(text))\n                if content_type is None:\n                    content_type = 'text/plain'\n                self.headers[hdrs.CONTENT_TYPE] = (\n                    content_type + '; charset=utf-8')\n                self._content_type = content_type\n                self._content_dict = {'charset': 'utf-8'}\n                self.body = text.encode('utf-8')\n            else:\n                self.text = text\n        else:\n            if content_type:\n                self.content_type = content_type\n            if body is not None:\n                self.body = body\n            else:\n                self.body = None\n\n    @property\n    def body(self):\n        return self._body\n\n    @body.setter\n    def body(self, body):\n        if body is not None and not isinstance(body, bytes):\n            raise TypeError('body argument must be bytes (%r)' % type(body))\n        self._body = body\n        if body is not None:\n            self.content_length = len(body)\n        else:\n            self.content_length = 0\n\n    @property\n    def text(self):\n        return self._body.decode(self.charset or 'utf-8')\n\n    @text.setter\n    def text(self, text):\n        if text is not None and not isinstance(text, str):\n            raise TypeError('text argument must be str (%r)' % type(text))\n\n        if self.content_type == 'application/octet-stream':\n            self.content_type = 'text/plain'\n        if self.charset is None:\n            self.charset = 'utf-8'\n\n        self.body = text.encode(self.charset)\n\n    @asyncio.coroutine\n    def write_eof(self):\n        body = self._body\n        if body is not None:\n            self.write(body)\n        yield from super().write_eof()\n\n\nclass WebSocketResponse(StreamResponse):\n\n    def __init__(self, *, protocols=()):\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._protocol = None\n        self._writer = None\n        self._reader = None\n        self._closing = False\n        self._loop = None\n        self._closing_fut = None\n\n    def start(self, request):\n        # make pre-check to don't hide it by do_handshake() exceptions\n        resp_impl = self._start_pre_check(request)\n        if resp_impl is not None:\n            return resp_impl\n\n        try:\n            status, headers, parser, writer, protocol = do_handshake(\n                request.method, request.headers, request.transport,\n                self._protocols)\n        except HttpProcessingError as err:\n            if err.code == 405:\n                raise HTTPMethodNotAllowed(request.method, ['GET'])\n            elif err.code == 400:\n                raise HTTPBadRequest(text=err.message, headers=err.headers)\n            else:  # pragma: no cover\n                raise HTTPInternalServerError() from err\n\n        if self.status != status:\n            self.set_status(status)\n        for k, v in headers:\n            self.headers[k] = v\n        self.force_close()\n\n        resp_impl = super().start(request)\n\n        self._reader = request._reader.set_parser(parser)\n        self._writer = writer\n        self._protocol = protocol\n        self._loop = request.app.loop\n        self._closing_fut = asyncio.Future(loop=self._loop)\n\n        return resp_impl\n\n    def can_start(self, request):\n        if self._writer is not None:\n            raise RuntimeError('Already started')\n        try:\n            _, _, _, _, protocol = do_handshake(\n                request.method, request.headers, request.transport,\n                self._protocols)\n        except HttpProcessingError:\n            return False, None\n        else:\n            return True, protocol\n\n    @property\n    def closing(self):\n        return self._closing\n\n    @property\n    def protocol(self):\n        return self._protocol\n\n    def ping(self, message='b'):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        self._writer.ping(message)\n\n    def pong(self, message='b'):\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        self._writer.pong(message)\n\n    def send_str(self, data):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        if not isinstance(data, str):\n            raise TypeError('data argument must be str (%r)' % type(data))\n        self._writer.send(data, binary=False)\n\n    def send_bytes(self, data):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)' %\n                            type(data))\n        self._writer.send(data, binary=True)\n\n    def close(self, *, code=1000, message=b''):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if not self._closing:\n            self._closing = True\n            self._writer.close(code, message)\n        else:\n            raise RuntimeError('Already closing')\n\n    @asyncio.coroutine\n    def wait_closed(self):\n        if self._closing_fut is None:\n            raise RuntimeError('Call .start() first')\n        yield from self._closing_fut\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self.wait_closed()\n        self._eof_sent = True\n\n    @asyncio.coroutine\n    def receive_msg(self):\n        if self._reader is None:\n            raise RuntimeError('Call .start() first')\n        while True:\n            try:\n                msg = yield from self._reader.read()\n            except Exception as exc:\n                self._closing_fut.set_exception(exc)\n                raise\n\n            if msg.tp == MSG_CLOSE:\n                if self._closing:\n                    exc = WSClientDisconnectedError(msg.data, msg.extra)\n                    self._closing_fut.set_exception(exc)\n                    raise exc\n                else:\n                    self._closing = True\n                    self._writer.close(msg.data, msg.extra)\n                    yield from self.drain()\n                    exc = WSClientDisconnectedError(msg.data, msg.extra)\n                    self._closing_fut.set_exception(exc)\n                    raise exc\n            elif not self._closing:\n                if msg.tp == MSG_PING:\n                    self._writer.pong(msg.data)\n                elif msg.tp in (MSG_TEXT, MSG_BINARY):\n                    return msg\n\n    @asyncio.coroutine\n    def receive_str(self):\n        msg = yield from self.receive_msg()\n        if msg.tp != MSG_TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not str\".format(msg.tp, msg.data))\n        return msg.data\n\n    @asyncio.coroutine\n    def receive_bytes(self):\n        msg = yield from self.receive_msg()\n        if msg.tp != MSG_BINARY:\n            raise TypeError(\n                \"Received message {}:{!r} is not bytes\".format(msg.tp,\n                                                               msg.data))\n        return msg.data\n\n    def write(self, data):\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n\n############################################################\n# HTTP Exceptions\n############################################################\n\nclass HTTPException(Response, Exception):\n\n    # You should set in subclasses:\n    # status = 200\n\n    status_code = None\n\n    def __init__(self, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        Response.__init__(self, status=self.status_code,\n                          headers=headers, reason=reason,\n                          body=body, text=text, content_type=content_type)\n        Exception.__init__(self, self.reason)\n        if self.body is None:\n            self.text = \"{}: {}\".format(self.status, self.reason)\n\n\nclass HTTPError(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 400s and 500s.\"\"\"\n\n\nclass HTTPRedirection(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 300s.\"\"\"\n\n\nclass HTTPSuccessful(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 200s.\"\"\"\n\n\nclass HTTPOk(HTTPSuccessful):\n    status_code = 200\n\n\nclass HTTPCreated(HTTPSuccessful):\n    status_code = 201\n\n\nclass HTTPAccepted(HTTPSuccessful):\n    status_code = 202\n\n\nclass HTTPNonAuthoritativeInformation(HTTPSuccessful):\n    status_code = 203\n\n\nclass HTTPNoContent(HTTPSuccessful):\n    status_code = 204\n\n\nclass HTTPResetContent(HTTPSuccessful):\n    status_code = 205\n\n\nclass HTTPPartialContent(HTTPSuccessful):\n    status_code = 206\n\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass _HTTPMove(HTTPRedirection):\n\n    def __init__(self, location, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Location'] = location\n        self.location = location\n\n\nclass HTTPMultipleChoices(_HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(_HTTPMove):\n    status_code = 301\n\n\nclass HTTPFound(_HTTPMove):\n    status_code = 302\n\n\n# This one is safe after a POST (the redirected location will be\n# retrieved with GET):\nclass HTTPSeeOther(_HTTPMove):\n    status_code = 303\n\n\nclass HTTPNotModified(HTTPRedirection):\n    # FIXME: this should include a date or etag header\n    status_code = 304\n\n\nclass HTTPUseProxy(_HTTPMove):\n    # Not a move, but looks a little like one\n    status_code = 305\n\n\nclass HTTPTemporaryRedirect(_HTTPMove):\n    status_code = 307\n\n\n############################################################\n# 4xx client error\n############################################################\n\n\nclass HTTPClientError(HTTPError):\n    pass\n\n\nclass HTTPBadRequest(HTTPClientError):\n    status_code = 400\n\n\nclass HTTPUnauthorized(HTTPClientError):\n    status_code = 401\n\n\nclass HTTPPaymentRequired(HTTPClientError):\n    status_code = 402\n\n\nclass HTTPForbidden(HTTPClientError):\n    status_code = 403\n\n\nclass HTTPNotFound(HTTPClientError):\n    status_code = 404\n\n\nclass HTTPMethodNotAllowed(HTTPClientError):\n    status_code = 405\n\n    def __init__(self, method, allowed_methods, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        allow = ','.join(sorted(allowed_methods))\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Allow'] = allow\n        self.allowed_methods = allowed_methods\n        self.method = method.upper()\n\n\nclass HTTPNotAcceptable(HTTPClientError):\n    status_code = 406\n\n\nclass HTTPProxyAuthenticationRequired(HTTPClientError):\n    status_code = 407\n\n\nclass HTTPRequestTimeout(HTTPClientError):\n    status_code = 408\n\n\nclass HTTPConflict(HTTPClientError):\n    status_code = 409\n\n\nclass HTTPGone(HTTPClientError):\n    status_code = 410\n\n\nclass HTTPLengthRequired(HTTPClientError):\n    status_code = 411\n\n\nclass HTTPPreconditionFailed(HTTPClientError):\n    status_code = 412\n\n\nclass HTTPRequestEntityTooLarge(HTTPClientError):\n    status_code = 413\n\n\nclass HTTPRequestURITooLong(HTTPClientError):\n    status_code = 414\n\n\nclass HTTPUnsupportedMediaType(HTTPClientError):\n    status_code = 415\n\n\nclass HTTPRequestRangeNotSatisfiable(HTTPClientError):\n    status_code = 416\n\n\nclass HTTPExpectationFailed(HTTPClientError):\n    status_code = 417\n\n\n############################################################\n# 5xx Server Error\n############################################################\n#  Response status codes beginning with the digit \"5\" indicate cases in\n#  which the server is aware that it has erred or is incapable of\n#  performing the request. Except when responding to a HEAD request, the\n#  server SHOULD include an entity containing an explanation of the error\n#  situation, and whether it is a temporary or permanent condition. User\n#  agents SHOULD display any included entity to the user. These response\n#  codes are applicable to any request method.\n\n\nclass HTTPServerError(HTTPError):\n    pass\n\n\nclass HTTPInternalServerError(HTTPServerError):\n    status_code = 500\n\n\nclass HTTPNotImplemented(HTTPServerError):\n    status_code = 501\n\n\nclass HTTPBadGateway(HTTPServerError):\n    status_code = 502\n\n\nclass HTTPServiceUnavailable(HTTPServerError):\n    status_code = 503\n\n\nclass HTTPGatewayTimeout(HTTPServerError):\n    status_code = 504\n\n\nclass HTTPVersionNotSupported(HTTPServerError):\n    status_code = 505\n\n\n############################################################\n# UrlDispatcher implementation\n############################################################\n\n\nclass UrlMappingMatchInfo(dict, AbstractMatchInfo):\n\n    def __init__(self, match_dict, route):\n        super().__init__(match_dict)\n        self._route = route\n\n    @property\n    def handler(self):\n        return self._route.handler\n\n    @property\n    def route(self):\n        return self._route\n\n    def __repr__(self):\n        return \"<MatchInfo {}: {}>\".format(super().__repr__(), self._route)\n\n\n@asyncio.coroutine\ndef defaultExpectHandler(request):\n    \"\"\"Default handler for Except: 100-continue\"\"\"\n    if request.version == HttpVersion11:\n        request.transport.write(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\")\n\n\nclass Route(metaclass=abc.ABCMeta):\n\n    def __init__(self, method, handler, name, expect_handler=None):\n        if expect_handler is None:\n            expect_handler = defaultExpectHandler\n        assert asyncio.iscoroutinefunction(expect_handler), \\\n            'Coroutine is expected, got {!r}'.format(expect_handler)\n\n        self._method = method\n        self._handler = handler\n        self._name = name\n        self._expect_handler = expect_handler\n\n    @property\n    def method(self):\n        return self._method\n\n    @property\n    def handler(self):\n        return self._handler\n\n    @property\n    def name(self):\n        return self._name\n\n    @abc.abstractmethod\n    def match(self, path):\n        \"\"\"Return dict with info for given path or\n        None if route cannot process path.\"\"\"\n\n    @abc.abstractmethod\n    def url(self, **kwargs):\n        \"\"\"Construct url for route with additional params.\"\"\"\n\n    @asyncio.coroutine\n    def handle_expect_header(self, request):\n        return (yield from self._expect_handler(request))\n\n    @staticmethod\n    def _append_query(url, query):\n        if query is not None:\n            return url + \"?\" + urlencode(query)\n        else:\n            return url\n\n\nclass PlainRoute(Route):\n\n    def __init__(self, method, handler, name, path, expect_handler=None):\n        super().__init__(method, handler, name, expect_handler=expect_handler)\n        self._path = path\n\n    def match(self, path):\n        # string comparison is about 10 times faster than regexp matching\n        if self._path == path:\n            return {}\n        else:\n            return None\n\n    def url(self, *, query=None):\n        return self._append_query(self._path, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<PlainRoute {name}[{method}] {path} -> {handler!r}\".format(\n            name=name, method=self.method, path=self._path,\n            handler=self.handler)\n\n\nclass DynamicRoute(Route):\n\n    def __init__(self, method, handler, name, pattern, formatter,\n                 expect_handler=None):\n        super().__init__(method, handler, name, expect_handler=expect_handler)\n        self._pattern = pattern\n        self._formatter = formatter\n\n    def match(self, path):\n        match = self._pattern.match(path)\n        if match is None:\n            return None\n        else:\n            return match.groupdict()\n\n    def url(self, *, parts, query=None):\n        url = self._formatter.format_map(parts)\n        return self._append_query(url, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return (\"<DynamicRoute {name}[{method}] {formatter} -> {handler!r}\"\n                .format(name=name, method=self.method,\n                        formatter=self._formatter, handler=self.handler))\n\n\nclass StaticRoute(Route):\n\n    limit = 8192\n\n    def __init__(self, name, prefix, directory, expect_handler=None):\n        assert prefix.startswith('/'), prefix\n        assert prefix.endswith('/'), prefix\n        super().__init__(\n            'GET', self.handle, name, expect_handler=expect_handler)\n        self._prefix = prefix\n        self._prefix_len = len(self._prefix)\n        self._directory = directory\n\n    def match(self, path):\n        if not path.startswith(self._prefix):\n            return None\n        return {'filename': path[self._prefix_len:]}\n\n    def url(self, *, filename, query=None):\n        while filename.startswith('/'):\n            filename = filename[1:]\n        url = self._prefix + filename\n        return self._append_query(url, query)\n\n    @asyncio.coroutine\n    def handle(self, request):\n        resp = StreamResponse()\n        filename = request.match_info['filename']\n        filepath = os.path.join(self._directory, filename)\n        if '..' in filename:\n            raise HTTPNotFound()\n        if not os.path.exists(filepath) or not os.path.isfile(filepath):\n            raise HTTPNotFound()\n\n        ct = mimetypes.guess_type(filename)[0]\n        if not ct:\n            ct = 'application/octet-stream'\n        resp.content_type = ct\n\n        file_size = os.stat(filepath).st_size\n        single_chunk = file_size < self.limit\n\n        if single_chunk:\n            resp.content_length = file_size\n        resp.start(request)\n\n        with open(filepath, 'rb') as f:\n            chunk = f.read(self.limit)\n            if single_chunk:\n                resp.write(chunk)\n            else:\n                while chunk:\n                    resp.write(chunk)\n                    chunk = f.read(self.limit)\n\n        return resp\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<StaticRoute {name}[{method}] {path} -> {directory!r}\".format(\n            name=name, method=self.method, path=self._prefix,\n            directory=self._directory)\n\n\nclass _NotFoundMatchInfo(UrlMappingMatchInfo):\n\n    def __init__(self):\n        super().__init__({}, None)\n\n    @property\n    def handler(self):\n        return self._not_found\n\n    @property\n    def route(self):\n        return None\n\n    @asyncio.coroutine\n    def _not_found(self, request):\n        raise HTTPNotFound()\n\n    def __repr__(self):\n        return \"<MatchInfo: not found>\"\n\n\nclass _MethodNotAllowedMatchInfo(UrlMappingMatchInfo):\n\n    def __init__(self, method, allowed_methods):\n        super().__init__({}, None)\n        self._method = method\n        self._allowed_methods = allowed_methods\n\n    @property\n    def handler(self):\n        return self._not_allowed\n\n    @property\n    def route(self):\n        return None\n\n    @asyncio.coroutine\n    def _not_allowed(self, request):\n        raise HTTPMethodNotAllowed(self._method, self._allowed_methods)\n\n    def __repr__(self):\n        return (\"<MatchInfo: method {} is not allowed (allowed methods: {}>\"\n                .format(self._method,\n                        ', '.join(sorted(self._allowed_methods))))\n\n\nclass UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    ROUTE_RE = re.compile(r'(\\{[_a-zA-Z][^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\})')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if not ('{' in path or '}' in path or self.ROUTE_RE.search(path)):\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n            self._register_endpoint(route)\n            return route\n\n        pattern = ''\n        formatter = ''\n        for part in self.ROUTE_RE.split(path):\n            match = self.DYN.match(part)\n            if match:\n                pattern += '(?P<' + match.group('var') + '>' + self.GOOD + ')'\n                formatter += '{%s}' % match.group('var')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                pattern += '(?P<%(var)s>%(re)s)' % match.groupdict()\n                formatter += '{%s}' % match.group('var')\n                continue\n\n            if '{' in part or '}' in part:\n                raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n\n            formatter += part\n            pattern += re.escape(part)\n\n        try:\n            compiled = re.compile('^' + pattern + '$')\n        except re.error as exc:\n            raise ValueError(\n                \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n        route = DynamicRoute(\n            method, handler, name, compiled,\n            formatter, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route\n\n\n############################################################\n# Application implementation\n############################################################\n\n\nclass RequestHandler(ServerHttpProtocol):\n\n    def __init__(self, manager, app, router, **kwargs):\n        super().__init__(**kwargs)\n\n        self._manager = manager\n        self._app = app\n        self._router = router\n        self._middlewares = app.middlewares\n\n    def connection_made(self, transport):\n        super().connection_made(transport)\n\n        self._manager.connection_made(self, transport)\n\n    def connection_lost(self, exc):\n        self._manager.connection_lost(self, exc)\n\n        super().connection_lost(exc)\n\n    @asyncio.coroutine\n    def handle_request(self, message, payload):\n        now = self._loop.time()\n\n        app = self._app\n        request = Request(app, message, payload,\n                          self.transport, self.reader, self.writer)\n        try:\n            match_info = yield from self._router.resolve(request)\n\n            assert isinstance(match_info, AbstractMatchInfo), match_info\n\n            resp = None\n            request._match_info = match_info\n            if request.headers.get(hdrs.EXPECT, '').lower() == \"100-continue\":\n                resp = (\n                    yield from match_info.route.handle_expect_header(request))\n\n            if resp is None:\n                handler = match_info.handler\n                for factory in reversed(self._middlewares):\n                    handler = yield from factory(app, handler)\n                resp = yield from handler(request)\n\n            if not isinstance(resp, StreamResponse):\n                raise RuntimeError(\n                    (\"Handler {!r} should return response instance, \"\n                     \"got {!r} [middlewares {!r}]\").format(\n                         match_info.handler,\n                         type(resp),\n                         self._middlewares))\n        except HTTPException as exc:\n            resp = exc\n\n        resp_msg = resp.start(request)\n        yield from resp.write_eof()\n\n        # notify server about keep-alive\n        self.keep_alive(resp_msg.keep_alive())\n\n        # log access\n        self.log_access(message, None, resp_msg, self._loop.time() - now)\n\n\nclass RequestHandlerFactory:\n\n    def __init__(self, app, router, *,\n                 handler=RequestHandler, loop=None, **kwargs):\n        self._app = app\n        self._router = router\n        self._handler = handler\n        self._loop = loop\n        self._connections = {}\n        self._kwargs = kwargs\n        self._kwargs.setdefault('logger', app.logger)\n\n    @property\n    def connections(self):\n        return list(self._connections.keys())\n\n    def connection_made(self, handler, transport):\n        self._connections[handler] = transport\n\n    def connection_lost(self, handler, exc=None):\n        if handler in self._connections:\n            del self._connections[handler]\n\n    @asyncio.coroutine\n    def finish_connections(self, timeout=None):\n        for handler in self._connections.keys():\n            handler.closing()\n\n        @asyncio.coroutine\n        def cleanup():\n            sleep = 0.05\n            while self._connections:\n                yield from asyncio.sleep(sleep, loop=self._loop)\n                if sleep < 5:\n                    sleep = sleep * 2\n\n        if timeout:\n            try:\n                yield from asyncio.wait_for(\n                    cleanup(), timeout, loop=self._loop)\n            except asyncio.TimeoutError:\n                self._app.logger.warning(\n                    \"Not all connections are closed (pending: %d)\",\n                    len(self._connections))\n\n        for transport in self._connections.values():\n            transport.close()\n\n        self._connections.clear()\n\n    def __call__(self):\n        return self._handler(\n            self, self._app, self._router, loop=self._loop, **self._kwargs)\n\n\nclass Application(dict):\n\n    def __init__(self, *, logger=web_logger, loop=None,\n                 router=None, handler_factory=RequestHandlerFactory,\n                 middlewares=()):\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if router is None:\n            router = UrlDispatcher()\n        assert isinstance(router, AbstractRouter), router\n\n        self._router = router\n        self._handler_factory = handler_factory\n        self._finish_callbacks = []\n        self._loop = loop\n        self.logger = logger\n\n        for factory in middlewares:\n            assert asyncio.iscoroutinefunction(factory), factory\n        self._middlewares = tuple(middlewares)\n\n    @property\n    def router(self):\n        return self._router\n\n    @property\n    def loop(self):\n        return self._loop\n\n    @property\n    def middlewares(self):\n        return self._middlewares\n\n    def make_handler(self, **kwargs):\n        return self._handler_factory(\n            self, self.router, loop=self.loop, **kwargs)\n\n    @asyncio.coroutine\n    def finish(self):\n        callbacks = self._finish_callbacks\n        self._finish_callbacks = []\n\n        for (cb, args, kwargs) in callbacks:\n            try:\n                res = cb(self, *args, **kwargs)\n                if (asyncio.iscoroutine(res) or\n                        isinstance(res, asyncio.Future)):\n                    yield from res\n            except Exception as exc:\n                self._loop.call_exception_handler({\n                    'message': \"Error in finish callback\",\n                    'exception': exc,\n                    'application': self,\n                })\n\n    def register_on_finish(self, func, *args, **kwargs):\n        self._finish_callbacks.insert(0, (func, args, kwargs))\n\n    def __call__(self):\n        \"\"\"gunicorn compatibility\"\"\"\n        return self\n\n    def __repr__(self):\n        return \"<Application>\"\n",
            "header": {
                "file": {
                    "path": "aiohttp/web.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "fe38569c",
                "new_commit_hash": "bb524284",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/web.py"
            },
            "plus_file": {
                "path": "b/aiohttp/web.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1383,
                            "length": 7
                        },
                        "new_range": {
                            "start": 1383,
                            "length": 7
                        },
                        "section": "class UrlDispatcher(AbstractRouter, collections.abc.Mapping):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    DYN_WITH_RE = re.compile(",
                                "type": "context"
                            },
                            {
                                "content": "        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')",
                                "type": "context"
                            },
                            {
                                "content": "    GOOD = r'[^{}/]+'",
                                "type": "context"
                            },
                            {
                                "content": "    PLAIN = re.compile('^' + GOOD + '$')",
                                "type": "deleted"
                            },
                            {
                                "content": "    ROUTE_RE = re.compile(r'(\\{[_a-zA-Z][^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\})')",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web.py",
                            "type": "class",
                            "name": "UrlDispatcher",
                            "content": "class UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n\n            self._register_endpoint(route)\n            return route\n\n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n        else:\n            pattern = '/' + '/'.join(parts)\n            formatter = '/' + '/'.join(format_parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n                formatter += '/'\n            try:\n                compiled = re.compile('^' + pattern + '$')\n            except re.error as exc:\n                raise ValueError(\n                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n            route = DynamicRoute(\n                method, handler, name, compiled,\n                formatter, expect_handler=expect_handler)\n\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 1380,
                            "end_lineno": 1516,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1444,
                            "length": 59
                        },
                        "new_range": {
                            "start": 1444,
                            "length": 41
                        },
                        "section": "class UrlDispatcher(AbstractRouter, collections.abc.Mapping):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        method = method.upper()",
                                "type": "context"
                            },
                            {
                                "content": "        assert method in self.METHODS, method",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        if isinstance(path, retype):",
                                "type": "deleted"
                            },
                            {
                                "content": "            route = DynamicRoute(",
                                "type": "deleted"
                            },
                            {
                                "content": "                method, handler, name, path,",
                                "type": "deleted"
                            },
                            {
                                "content": "                path.pattern, expect_handler=expect_handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        if not ('{' in path or '}' in path or self.ROUTE_RE.search(path)):",
                                "type": "added"
                            },
                            {
                                "content": "            route = PlainRoute(",
                                "type": "added"
                            },
                            {
                                "content": "                method, handler, name, path, expect_handler=expect_handler)",
                                "type": "added"
                            },
                            {
                                "content": "            self._register_endpoint(route)",
                                "type": "context"
                            },
                            {
                                "content": "            return route",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        assert path.startswith('/')",
                                "type": "deleted"
                            },
                            {
                                "content": "        parts = []",
                                "type": "deleted"
                            },
                            {
                                "content": "        factory = PlainRoute",
                                "type": "deleted"
                            },
                            {
                                "content": "        format_parts = []",
                                "type": "deleted"
                            },
                            {
                                "content": "        for part in path.split('/'):",
                                "type": "deleted"
                            },
                            {
                                "content": "            if not part:",
                                "type": "deleted"
                            },
                            {
                                "content": "                continue",
                                "type": "deleted"
                            },
                            {
                                "content": "        pattern = ''",
                                "type": "added"
                            },
                            {
                                "content": "        formatter = ''",
                                "type": "added"
                            },
                            {
                                "content": "        for part in self.ROUTE_RE.split(path):",
                                "type": "added"
                            },
                            {
                                "content": "            match = self.DYN.match(part)",
                                "type": "context"
                            },
                            {
                                "content": "            if match:",
                                "type": "context"
                            },
                            {
                                "content": "                parts.append('(?P<' + match.group('var') + '>' +",
                                "type": "deleted"
                            },
                            {
                                "content": "                             self.GOOD + ')')",
                                "type": "deleted"
                            },
                            {
                                "content": "                factory = DynamicRoute",
                                "type": "deleted"
                            },
                            {
                                "content": "                format_parts.append('{'+match.group('var')+'}')",
                                "type": "deleted"
                            },
                            {
                                "content": "                pattern += '(?P<' + match.group('var') + '>' + self.GOOD + ')'",
                                "type": "added"
                            },
                            {
                                "content": "                formatter += '{%s}' % match.group('var')",
                                "type": "added"
                            },
                            {
                                "content": "                continue",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "            match = self.DYN_WITH_RE.match(part)",
                                "type": "context"
                            },
                            {
                                "content": "            if match:",
                                "type": "context"
                            },
                            {
                                "content": "                parts.append('(?P<' + match.group('var') + '>' +",
                                "type": "deleted"
                            },
                            {
                                "content": "                             match.group('re') + ')')",
                                "type": "deleted"
                            },
                            {
                                "content": "                factory = DynamicRoute",
                                "type": "deleted"
                            },
                            {
                                "content": "                format_parts.append('{'+match.group('var')+'}')",
                                "type": "deleted"
                            },
                            {
                                "content": "                continue",
                                "type": "deleted"
                            },
                            {
                                "content": "            if self.PLAIN.match(part):",
                                "type": "deleted"
                            },
                            {
                                "content": "                parts.append(re.escape(part))",
                                "type": "deleted"
                            },
                            {
                                "content": "                format_parts.append(part)",
                                "type": "deleted"
                            },
                            {
                                "content": "                pattern += '(?P<%(var)s>%(re)s)' % match.groupdict()",
                                "type": "added"
                            },
                            {
                                "content": "                formatter += '{%s}' % match.group('var')",
                                "type": "added"
                            },
                            {
                                "content": "                continue",
                                "type": "context"
                            },
                            {
                                "content": "            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))",
                                "type": "deleted"
                            },
                            {
                                "content": "        if factory is PlainRoute:",
                                "type": "deleted"
                            },
                            {
                                "content": "            route = PlainRoute(",
                                "type": "deleted"
                            },
                            {
                                "content": "                method, handler, name, path, expect_handler=expect_handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "        else:",
                                "type": "deleted"
                            },
                            {
                                "content": "            pattern = '/' + '/'.join(parts)",
                                "type": "deleted"
                            },
                            {
                                "content": "            formatter = '/' + '/'.join(format_parts)",
                                "type": "deleted"
                            },
                            {
                                "content": "            if path.endswith('/') and pattern != '/':",
                                "type": "deleted"
                            },
                            {
                                "content": "                pattern += '/'",
                                "type": "deleted"
                            },
                            {
                                "content": "                formatter += '/'",
                                "type": "deleted"
                            },
                            {
                                "content": "            try:",
                                "type": "deleted"
                            },
                            {
                                "content": "                compiled = re.compile('^' + pattern + '$')",
                                "type": "deleted"
                            },
                            {
                                "content": "            except re.error as exc:",
                                "type": "deleted"
                            },
                            {
                                "content": "                raise ValueError(",
                                "type": "deleted"
                            },
                            {
                                "content": "                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None",
                                "type": "deleted"
                            },
                            {
                                "content": "            route = DynamicRoute(",
                                "type": "deleted"
                            },
                            {
                                "content": "                method, handler, name, compiled,",
                                "type": "deleted"
                            },
                            {
                                "content": "                formatter, expect_handler=expect_handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "            if '{' in part or '}' in part:",
                                "type": "added"
                            },
                            {
                                "content": "                raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "            formatter += part",
                                "type": "added"
                            },
                            {
                                "content": "            pattern += re.escape(part)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        try:",
                                "type": "added"
                            },
                            {
                                "content": "            compiled = re.compile('^' + pattern + '$')",
                                "type": "added"
                            },
                            {
                                "content": "        except re.error as exc:",
                                "type": "added"
                            },
                            {
                                "content": "            raise ValueError(",
                                "type": "added"
                            },
                            {
                                "content": "                \"Bad pattern '{}': {}\".format(pattern, exc)) from None",
                                "type": "added"
                            },
                            {
                                "content": "        route = DynamicRoute(",
                                "type": "added"
                            },
                            {
                                "content": "            method, handler, name, compiled,",
                                "type": "added"
                            },
                            {
                                "content": "            formatter, expect_handler=expect_handler)",
                                "type": "added"
                            },
                            {
                                "content": "        self._register_endpoint(route)",
                                "type": "context"
                            },
                            {
                                "content": "        return route",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web.py",
                            "type": "method",
                            "name": "UrlDispatcher.add_route",
                            "content": "class UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n\n            self._register_endpoint(route)\n            return route\n\n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n        else:\n            pattern = '/' + '/'.join(parts)\n            formatter = '/' + '/'.join(format_parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n                formatter += '/'\n            try:\n                compiled = re.compile('^' + pattern + '$')\n            except re.error as exc:\n                raise ValueError(\n                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n            route = DynamicRoute(\n                method, handler, name, compiled,\n                formatter, expect_handler=expect_handler)\n\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 1438,
                            "end_lineno": 1501,
                            "parent": {
                                "file_name": "aiohttp/web.py",
                                "type": "class",
                                "name": "UrlDispatcher",
                                "content": "class UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n\n            self._register_endpoint(route)\n            return route\n\n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n        else:\n            pattern = '/' + '/'.join(parts)\n            formatter = '/' + '/'.join(format_parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n                formatter += '/'\n            try:\n                compiled = re.compile('^' + pattern + '$')\n            except re.error as exc:\n                raise ValueError(\n                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n            route = DynamicRoute(\n                method, handler, name, compiled,\n                formatter, expect_handler=expect_handler)\n\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 1380,
                                "end_lineno": 1516,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "aiohttp/web.py",
                            "type": "class",
                            "name": "UrlDispatcher",
                            "content": "class UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n\n            self._register_endpoint(route)\n            return route\n\n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n        else:\n            pattern = '/' + '/'.join(parts)\n            formatter = '/' + '/'.join(format_parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n                formatter += '/'\n            try:\n                compiled = re.compile('^' + pattern + '$')\n            except re.error as exc:\n                raise ValueError(\n                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n            route = DynamicRoute(\n                method, handler, name, compiled,\n                formatter, expect_handler=expect_handler)\n\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 1380,
                            "end_lineno": 1516,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport os\nimport unittest\nfrom unittest import mock\nimport aiohttp.web\nfrom aiohttp.web import (UrlDispatcher, Request, Response,\n                         HTTPMethodNotAllowed, HTTPNotFound)\nfrom aiohttp.multidict import CIMultiDict\nfrom aiohttp.protocol import HttpVersion, RawRequestMessage\n\n\nclass TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')\n",
            "new_file_content": "import asyncio\nimport os\nimport unittest\nfrom unittest import mock\nimport aiohttp.web\nfrom aiohttp.web import (UrlDispatcher, Request, Response,\n                         HTTPMethodNotAllowed, HTTPNotFound)\nfrom aiohttp.multidict import CIMultiDict\nfrom aiohttp.protocol import HttpVersion, RawRequestMessage\n\n\nclass TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_and_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)\n        req = self.make_request('GET', '/handler/1234/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/'}, info)\n\n        self.router.add_route('GET', r'/handler/{to:.+}', handler)\n        req = self.make_request('GET', '/handler/1234/5/6/7')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/5/6/7'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n",
            "header": {
                "file": {
                    "path": "tests/test_urldispatch.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "2f7e102d",
                "new_commit_hash": "eea66448",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_urldispatch.py"
            },
            "plus_file": {
                "path": "b/tests/test_urldispatch.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 112,
                            "length": 11
                        },
                        "new_range": {
                            "start": 112,
                            "length": 6
                        },
                        "section": "class TestUrlDispatcher(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        with self.assertRaises(ValueError):",
                                "type": "context"
                            },
                            {
                                "content": "            self.router.add_route('post', '/post/{id\"}', handler)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_add_url_invalid5(self):",
                                "type": "deleted"
                            },
                            {
                                "content": "        handler = self.make_handler()",
                                "type": "deleted"
                            },
                            {
                                "content": "        with self.assertRaises(ValueError):",
                                "type": "deleted"
                            },
                            {
                                "content": "            self.router.add_route('post', '/post\"{id}', handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "    def test_add_url_escaping(self):",
                                "type": "context"
                            },
                            {
                                "content": "        handler = self.make_handler()",
                                "type": "context"
                            },
                            {
                                "content": "        self.router.add_route('GET', '/+$', handler)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "class",
                            "name": "TestUrlDispatcher",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 12,
                            "end_lineno": 445,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "method",
                            "name": "TestUrlDispatcher.test_add_url_invalid5",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 115,
                            "end_lineno": 118,
                            "parent": {
                                "file_name": "tests/test_urldispatch.py",
                                "type": "class",
                                "name": "TestUrlDispatcher",
                                "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 12,
                                "end_lineno": 445,
                                "parent": null
                            }
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 298,
                            "length": 6
                        },
                        "new_range": {
                            "start": 293,
                            "length": 20
                        },
                        "section": "class TestUrlDispatcher(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.assertIsNotNone(info)",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertEqual({'to': '1234'}, info)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_add_route_with_re_and_slashes(self):",
                                "type": "added"
                            },
                            {
                                "content": "        handler = self.make_handler()",
                                "type": "added"
                            },
                            {
                                "content": "        self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)",
                                "type": "added"
                            },
                            {
                                "content": "        req = self.make_request('GET', '/handler/1234/')",
                                "type": "added"
                            },
                            {
                                "content": "        info = self.loop.run_until_complete(self.router.resolve(req))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertIsNotNone(info)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual({'to': '1234/'}, info)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.router.add_route('GET', r'/handler/{to:.+}', handler)",
                                "type": "added"
                            },
                            {
                                "content": "        req = self.make_request('GET', '/handler/1234/5/6/7')",
                                "type": "added"
                            },
                            {
                                "content": "        info = self.loop.run_until_complete(self.router.resolve(req))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertIsNotNone(info)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual({'to': '1234/5/6/7'}, info)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_add_route_with_re_not_match(self):",
                                "type": "context"
                            },
                            {
                                "content": "        handler = self.make_handler()",
                                "type": "context"
                            },
                            {
                                "content": "        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "class",
                            "name": "TestUrlDispatcher",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_and_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)\n        req = self.make_request('GET', '/handler/1234/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/'}, info)\n\n        self.router.add_route('GET', r'/handler/{to:.+}', handler)\n        req = self.make_request('GET', '/handler/1234/5/6/7')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/5/6/7'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 12,
                            "end_lineno": 437,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "method",
                            "name": "TestUrlDispatcher.test_add_route_with_re_and_slashes",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_and_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)\n        req = self.make_request('GET', '/handler/1234/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/'}, info)\n\n        self.router.add_route('GET', r'/handler/{to:.+}', handler)\n        req = self.make_request('GET', '/handler/1234/5/6/7')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/5/6/7'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 296,
                            "end_lineno": 308,
                            "parent": {
                                "file_name": "tests/test_urldispatch.py",
                                "type": "class",
                                "name": "TestUrlDispatcher",
                                "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_and_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)\n        req = self.make_request('GET', '/handler/1234/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/'}, info)\n\n        self.router.add_route('GET', r'/handler/{to:.+}', handler)\n        req = self.make_request('GET', '/handler/1234/5/6/7')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/5/6/7'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 12,
                                "end_lineno": 437,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 322,
                            "length": 9
                        },
                        "new_range": {
                            "start": 331,
                            "length": 9
                        },
                        "section": "class TestUrlDispatcher(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        handler = self.make_handler()",
                                "type": "context"
                            },
                            {
                                "content": "        with self.assertRaises(ValueError) as ctx:",
                                "type": "context"
                            },
                            {
                                "content": "            self.router.add_route('GET', r'/handler/{to:+++}', handler)",
                                "type": "context"
                            },
                            {
                                "content": "        s = str(ctx.exception)",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertTrue(s.startswith(",
                                "type": "deleted"
                            },
                            {
                                "content": "            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertEqual(",
                                "type": "added"
                            },
                            {
                                "content": "            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",",
                                "type": "added"
                            },
                            {
                                "content": "            str(ctx.exception))",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertIsNone(ctx.exception.__cause__)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_route_dynamic_with_regex_spec(self):",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "method",
                            "name": "TestUrlDispatcher.test_add_route_with_invalid_re",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 321,
                            "end_lineno": 328,
                            "parent": {
                                "file_name": "tests/test_urldispatch.py",
                                "type": "class",
                                "name": "TestUrlDispatcher",
                                "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 12,
                                "end_lineno": 445,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "class",
                            "name": "TestUrlDispatcher",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 12,
                            "end_lineno": 445,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 426,
                            "length": 20
                        },
                        "new_range": {
                            "start": 435,
                            "length": 3
                        },
                        "section": "class TestUrlDispatcher(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.assertRaises(",
                                "type": "context"
                            },
                            {
                                "content": "            AssertionError, self.router.add_route,",
                                "type": "context"
                            },
                            {
                                "content": "            'GET', '/', self.make_handler(), expect_handler=handler)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "    def test_raw_regexp(self):",
                                "type": "deleted"
                            },
                            {
                                "content": "        import re",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        handler = self.make_handler()",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "        req = self.make_request('GET', '/test/foo/bar')",
                                "type": "deleted"
                            },
                            {
                                "content": "        info = self.loop.run_until_complete(self.router.resolve(req))",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertIsNotNone(info)",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertEqual(0, len(info))",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertIs(handler, info.handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertIsNone(info.route.name)",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)",
                                "type": "deleted"
                            },
                            {
                                "content": "        req = self.make_request('GET', '/var/foo/bar')",
                                "type": "deleted"
                            },
                            {
                                "content": "        info = self.loop.run_until_complete(self.router.resolve(req))",
                                "type": "deleted"
                            },
                            {
                                "content": "        self.assertEqual(info.get('var'), 'foo/bar')",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "class",
                            "name": "TestUrlDispatcher",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 12,
                            "end_lineno": 445,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": [
                        {
                            "file_name": "tests/test_urldispatch.py",
                            "type": "method",
                            "name": "TestUrlDispatcher.test_raw_regexp",
                            "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 430,
                            "end_lineno": 445,
                            "parent": {
                                "file_name": "tests/test_urldispatch.py",
                                "type": "class",
                                "name": "TestUrlDispatcher",
                                "content": "class TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_invalid5(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post\"{id}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        s = str(ctx.exception)\n        self.assertTrue(s.startswith(\n            \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat\"), s)\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n\n    def test_raw_regexp(self):\n        import re\n\n        handler = self.make_handler()\n        self.router.add_route('GET', re.compile('^/test/(.*)$'), handler)\n        req = self.make_request('GET', '/test/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n        self.router.add_route('GET', re.compile('^/var/(?P<var>.*)$'), handler)\n        req = self.make_request('GET', '/var/foo/bar')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertEqual(info.get('var'), 'foo/bar')",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 12,
                                "end_lineno": 445,
                                "parent": null
                            }
                        }
                    ]
                }
            ]
        }
    ],
    "old_commit_hash": "f4b5552c49a43359d3e2a14dc7d0d44262f9e41f^",
    "new_commit_hash": "f4b5552c49a43359d3e2a14dc7d0d44262f9e41f",
    "commit_message": "Refactor URL Dispatcher",
    "commit_date": "2015-03-13T19:45:37+03:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "f4b5552c49a43359d3e2a14dc7d0d44262f9e41f",
    "test_file_codes": [
        "import asyncio\nimport os\nimport unittest\nfrom unittest import mock\nimport aiohttp.web\nfrom aiohttp.web import (UrlDispatcher, Request, Response,\n                         HTTPMethodNotAllowed, HTTPNotFound)\nfrom aiohttp.multidict import CIMultiDict\nfrom aiohttp.protocol import HttpVersion, RawRequestMessage\n\n\nclass TestUrlDispatcher(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        self.router = UrlDispatcher()\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion(1, 1),\n                                    CIMultiDict(), False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.reader = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.reader, self.writer)\n        return req\n\n    def make_handler(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            return Response(request)  # pragma: no cover\n\n        return handler\n\n    def test_add_route_root(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/', handler)\n        req = self.make_request('GET', '/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_route_simple(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler)\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual(0, len(info))\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_matchdict(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/{to}', handler)\n        req = self.make_request('GET', '/handler/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 'tail'}, info)\n        self.assertIs(handler, info.handler)\n        self.assertIsNone(info.route.name)\n\n    def test_add_with_name(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path', handler,\n                              name='name')\n        req = self.make_request('GET', '/handler/to/path')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual('name', info.route.name)\n\n    def test_add_with_tailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/handler/to/path/', handler)\n        req = self.make_request('GET', '/handler/to/path/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler, info.handler)\n\n    def test_add_invalid_path(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('GET', '/{/', handler)\n\n    def test_add_url_invalid1(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id', handler)\n\n    def test_add_url_invalid2(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}}', handler)\n\n    def test_add_url_invalid3(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id{}', handler)\n\n    def test_add_url_invalid4(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError):\n            self.router.add_route('post', '/post/{id\"}', handler)\n\n    def test_add_url_escaping(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/+$', handler)\n\n        req = self.make_request('GET', '/+$')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertIs(handler, info.handler)\n\n    def test_match_second_result_in_table(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/h1', handler1)\n        self.router.add_route('POST', '/h2', handler2)\n        req = self.make_request('POST', '/h2')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({}, info)\n        self.assertIs(handler2, info.handler)\n\n    def test_raise_method_not_allowed(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/', handler1)\n        self.router.add_route('POST', '/', handler2)\n        req = self.make_request('PUT', '/')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPMethodNotAllowed) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual('PUT', exc.method)\n        self.assertEqual(405, exc.status)\n        self.assertEqual({'POST', 'GET'}, exc.allowed_methods)\n\n    def test_raise_method_not_found(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/a', handler)\n        req = self.make_request('GET', '/b')\n\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n\n        with self.assertRaises(HTTPNotFound) as ctx:\n            self.loop.run_until_complete(match_info.handler(req))\n\n        exc = ctx.exception\n        self.assertEqual(404, exc.status)\n\n    def test_double_add_url_with_the_same_name(self):\n        handler1 = self.make_handler()\n        handler2 = self.make_handler()\n        self.router.add_route('GET', '/get', handler1, name='name')\n\n        regexp = (\"Duplicate 'name', already handled by\")\n        with self.assertRaisesRegex(ValueError, regexp):\n            self.router.add_route('GET', '/get_other', handler2, name='name')\n\n    def test_route_plain(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get', handler, name='name')\n        route2 = self.router['name']\n        url = route2.url()\n        self.assertEqual('/get', url)\n        self.assertIs(route, route2)\n\n    def test_route_unknown_route_name(self):\n        with self.assertRaises(KeyError):\n            self.router['unknown']\n\n    def test_route_dynamic(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{name}', handler,\n                                      name='name')\n\n        route2 = self.router['name']\n        url = route2.url(parts={'name': 'John'})\n        self.assertEqual('/get/John', url)\n        self.assertIs(route, route2)\n\n    def test_route_with_qs(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get', handler, name='name')\n\n        url = self.router['name'].url(query=[('a', 'b'), ('c', 1)])\n        self.assertEqual('/get?a=b&c=1', url)\n\n    def test_add_static(self):\n        route = self.router.add_static('/st',\n                                       os.path.dirname(aiohttp.__file__),\n                                       name='static')\n        route2 = self.router['static']\n        url = route2.url(filename='/dir/a.txt')\n        self.assertEqual('/st/dir/a.txt', url)\n        self.assertIs(route, route2)\n\n    def test_plain_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}', handler, name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_static_not_match(self):\n        self.router.add_static('/pre', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        route = self.router['name']\n        self.assertIsNone(route.match('/another/path'))\n\n    def test_dynamic_with_trailing_slash(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{name}/', handler, name='name')\n        route = self.router['name']\n        self.assertEqual({'name': 'John'}, route.match('/get/John/'))\n\n    def test_len(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual(2, len(self.router))\n\n    def test_iter(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertEqual({'name1', 'name2'}, set(iter(self.router)))\n\n    def test_contains(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get1', handler, name='name1')\n        self.router.add_route('GET', '/get2', handler, name='name2')\n        self.assertIn('name1', self.router)\n        self.assertNotIn('name3', self.router)\n\n    def test_plain_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/path', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<PlainRoute 'name' \\[GET\\] /get/path\")\n\n    def test_dynamic_repr(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', '/get/{path}', handler, name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<DynamicRoute 'name' \\[GET\\] /get/{path}\")\n\n    def test_static_repr(self):\n        self.router.add_static('/get', os.path.dirname(aiohttp.__file__),\n                               name='name')\n        self.assertRegex(repr(self.router['name']),\n                         r\"<StaticRoute 'name' \\[GET\\] /get/\")\n\n    def test_static_adds_slash(self):\n        route = self.router.add_static('/prefix',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_static_dont_add_trailing_slash(self):\n        route = self.router.add_static('/prefix/',\n                                       os.path.dirname(aiohttp.__file__))\n        self.assertEqual('/prefix/', route._prefix)\n\n    def test_add_route_with_re(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/1234')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234'}, info)\n\n    def test_add_route_with_re_and_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)\n        req = self.make_request('GET', '/handler/1234/')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/'}, info)\n\n        self.router.add_route('GET', r'/handler/{to:.+}', handler)\n        req = self.make_request('GET', '/handler/1234/5/6/7')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': '1234/5/6/7'}, info)\n\n    def test_add_route_with_re_not_match(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:\\d+}', handler)\n\n        req = self.make_request('GET', '/handler/tail')\n        match_info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNone(match_info.route)\n        self.assertEqual({}, match_info)\n        with self.assertRaises(HTTPNotFound):\n            self.loop.run_until_complete(match_info.handler(req))\n\n    def test_add_route_with_re_including_slashes(self):\n        handler = self.make_handler()\n        self.router.add_route('GET', r'/handler/{to:.+}/tail', handler)\n\n        req = self.make_request('GET', '/handler/re/with/slashes/tail')\n        info = self.loop.run_until_complete(self.router.resolve(req))\n        self.assertIsNotNone(info)\n        self.assertEqual({'to': 're/with/slashes'}, info)\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n        self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\n        self.assertIsNone(ctx.exception.__cause__)\n\n    def test_route_dynamic_with_regex_spec(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123', url)\n\n    def test_route_dynamic_with_regex_spec_and_trailing_slash(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', '/get/{num:^\\d+}/', handler,\n                                      name='name')\n\n        url = route.url(parts={'num': '123'})\n        self.assertEqual('/get/123/', url)\n\n    def test_route_dynamic_with_regex(self):\n        handler = self.make_handler()\n        route = self.router.add_route('GET', r'/{one}/{two:.+}', handler)\n\n        url = route.url(parts={'one': 1, 'two': 2})\n        self.assertEqual('/1/2', url)\n\n    def test_regular_match_info(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/get/{name}', handler)\n\n            req = self.make_request('GET', '/get/john')\n            match_info = yield from self.router.resolve(req)\n            self.maxDiff = None\n            self.assertRegex(repr(match_info),\n                             \"<MatchInfo {'name': 'john'}: <DynamicRoute.+>>\")\n\n        self.loop.run_until_complete(go())\n\n    def test_not_found_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            req = self.make_request('POST', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: not found>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_not_allowed_repr(self):\n\n        @asyncio.coroutine\n        def go():\n            handler = self.make_handler()\n            self.router.add_route('GET', '/path/to', handler)\n\n            handler2 = self.make_handler()\n            self.router.add_route('POST', '/path/to', handler2)\n\n            req = self.make_request('PUT', '/path/to')\n            match_info = yield from self.router.resolve(req)\n            self.assertEqual(\"<MatchInfo: method PUT is not allowed \"\n                             \"(allowed methods: GET, POST>\", repr(match_info))\n\n        self.loop.run_until_complete(go())\n\n    def test_default_expect_handler(self):\n        route = self.router.add_route('GET', '/', self.make_handler())\n        self.assertIs(route._expect_handler, aiohttp.web.defaultExpectHandler)\n\n    def test_custom_expect_handler_plain(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.PlainRoute)\n\n    def test_custom_expect_handler_dynamic(self):\n\n        @asyncio.coroutine\n        def handler(request):\n            pass\n\n        route = self.router.add_route(\n            'GET', '/get/{name}', self.make_handler(), expect_handler=handler)\n        self.assertIs(route._expect_handler, handler)\n        self.assertIsInstance(route, aiohttp.web.DynamicRoute)\n\n    def test_expect_handler_non_coroutine(self):\n\n        def handler(request):\n            pass\n\n        self.assertRaises(\n            AssertionError, self.router.add_route,\n            'GET', '/', self.make_handler(), expect_handler=handler)\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Transforming aiohttp/worker.py\nTransforming aiohttp/server.py\nTransforming aiohttp/client.py\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nmake: *** No rule to make target '.develop'.  Stop.\nResolved 26 packages in 64ms\nInstalled 26 packages in 46ms\n + async-generator==1.10\n + async-timeout==5.0.1\n + brotlipy==0.7.0\n + cffi==1.17.1\n + chardet==5.2.0\n + coverage==7.6.10\n + cryptography==44.0.0\n + cython==3.0.11\n + exceptiongroup==1.2.2\n + gunicorn==23.0.0\n + idna==3.10\n + iniconfig==2.0.0\n + multdict==0.0.5\n + multidict==6.1.0\n + packaging==24.2\n + pluggy==1.5.0\n + propcache==0.2.1\n + pycparser==2.22\n + pytest==8.3.4\n + pytest-asyncio==0.25.1\n + pytest-cov==6.0.0\n + pytest-mock==3.14.0\n + tomli==2.2.1\n + trustme==1.2.1\n + typing-extensions==4.12.2\n + yarl==1.18.3\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_f4b5552c49a43359d3e2a14dc7d0d44262f9e41f\nplugins: mock-3.14.0, asyncio-0.25.1, cov-6.0.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollected 47 items\n\nr2e_tests/test_1.py ...F...........................................      [100%]\n\n=================================== FAILURES ===================================\n_______________ TestUrlDispatcher.test_add_route_with_invalid_re _______________\n\nself = <r2e_tests.test_1.TestUrlDispatcher testMethod=test_add_route_with_invalid_re>\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n>       self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\nE       AssertionError: \"Bad pattern '\\\\/handler\\\\/(?P<to>+++)': nothing to repeat\" != \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat at position 17\"\nE       - Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\nE       ?              -        -\nE       + Bad pattern '/handler/(?P<to>+++)': nothing to repeat at position 17\nE       ?                                                      +++++++++++++++\n\nr2e_tests/test_1.py:334: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_invalid_path\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_root\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_simple\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re_and_slashes\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re_including_slashes\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_static\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_escaping\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid1\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid2\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid3\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid4\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_with_matchdict\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_with_name\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_with_tailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_contains\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_custom_expect_handler_dynamic\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_custom_expect_handler_plain\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_default_expect_handler\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_double_add_url_with_the_same_name\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_dynamic_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_dynamic_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_dynamic_with_trailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_expect_handler_non_coroutine\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_iter\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_len\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_match_second_result_in_table\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_not_allowed_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_not_found_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_plain_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_plain_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_raise_method_not_allowed\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_raise_method_not_found\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_regular_match_info\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic_with_regex\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic_with_regex_spec\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic_with_regex_spec_and_trailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_plain\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_unknown_route_name\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_with_qs\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_adds_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_dont_add_trailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_repr\nFAILED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_invalid_re\n========================= 1 failed, 46 passed in 0.54s =========================\n",
    "new_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_f4b5552c49a43359d3e2a14dc7d0d44262f9e41f/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_f4b5552c49a43359d3e2a14dc7d0d44262f9e41f\nplugins: mock-3.14.0, asyncio-0.25.1, cov-6.0.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollected 47 items\n\nr2e_tests/test_1.py ...F.F.........................................      [100%]\n\n=================================== FAILURES ===================================\n_______________ TestUrlDispatcher.test_add_route_with_invalid_re _______________\n\nself = <r2e_tests.test_1.TestUrlDispatcher testMethod=test_add_route_with_invalid_re>\n\n    def test_add_route_with_invalid_re(self):\n        handler = self.make_handler()\n        with self.assertRaises(ValueError) as ctx:\n            self.router.add_route('GET', r'/handler/{to:+++}', handler)\n>       self.assertEqual(\n            \"Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\",\n            str(ctx.exception))\nE       AssertionError: \"Bad pattern '\\\\/handler\\\\/(?P<to>+++)': nothing to repeat\" != \"Bad pattern '/handler/(?P<to>+++)': nothing to repeat at position 17\"\nE       - Bad pattern '\\/handler\\/(?P<to>+++)': nothing to repeat\nE       ?              -        -\nE       + Bad pattern '/handler/(?P<to>+++)': nothing to repeat at position 17\nE       ?                                                      +++++++++++++++\n\nr2e_tests/test_1.py:334: AssertionError\n_____________ TestUrlDispatcher.test_add_route_with_re_and_slashes _____________\n\nself = <r2e_tests.test_1.TestUrlDispatcher testMethod=test_add_route_with_re_and_slashes>\n\n    def test_add_route_with_re_and_slashes(self):\n        handler = self.make_handler()\n>       self.router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)\n\nr2e_tests/test_1.py:298: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <aiohttp.web.UrlDispatcher object at 0x7f1da33215b0>, method = 'GET'\npath = '/handler/{to:[^/]+/?}'\nhandler = <function TestUrlDispatcher.make_handler.<locals>.handler at 0x7f1da321f0d0>\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n    \n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n    \n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n    \n            self._register_endpoint(route)\n            return route\n    \n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n    \n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n>           raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\nE           ValueError: Invalid path '/handler/{to:[^/]+/?}'['{to:[^']\n\naiohttp/web.py:1481: ValueError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_invalid_path\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_root\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_simple\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re_including_slashes\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_static\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_escaping\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid1\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid2\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid3\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_url_invalid4\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_with_matchdict\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_with_name\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_add_with_tailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_contains\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_custom_expect_handler_dynamic\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_custom_expect_handler_plain\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_default_expect_handler\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_double_add_url_with_the_same_name\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_dynamic_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_dynamic_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_dynamic_with_trailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_expect_handler_non_coroutine\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_iter\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_len\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_match_second_result_in_table\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_not_allowed_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_not_found_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_plain_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_plain_repr\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_raise_method_not_allowed\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_raise_method_not_found\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_regular_match_info\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic_with_regex\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic_with_regex_spec\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_dynamic_with_regex_spec_and_trailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_plain\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_unknown_route_name\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_route_with_qs\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_adds_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_dont_add_trailing_slash\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_not_match\nPASSED r2e_tests/test_1.py::TestUrlDispatcher::test_static_repr\nFAILED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_invalid_re\nFAILED r2e_tests/test_1.py::TestUrlDispatcher::test_add_route_with_re_and_slashes\n========================= 2 failed, 45 passed in 0.19s =========================\n",
    "old_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_f4b5552c49a43359d3e2a14dc7d0d44262f9e41f/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n"
}
aiohttp/web.py tests/test_urldispatch.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Adding Route with Regex and Optional Trailing Slash Raises ValueError

**Description:**
When attempting to add a route that includes a regular expression with an optional trailing slash, the URL dispatcher raises a `ValueError`. This prevents the route from being registered correctly, disrupting the application's routing functionality.

**Example Code:**
```python
handler = make_handler()
router.add_route('GET', '/handler/{to:[^/]+/?}', handler)
```

**Error Message:**
```
ValueError: Invalid path '/handler/{to:[^/]+/?}'['{to:[^']
```

**Expected Behavior:**
The route should be added successfully, allowing URLs that match the pattern with or without a trailing slash. For example, both `/handler/1234` and `/handler/1234/` should be valid and correctly routed to the specified handler.

**Actual Behavior:**
Adding the route raises a `ValueError`, indicating that the provided path is invalid. This error prevents the route from being registered, causing any matching URLs to fail to resolve correctly.

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

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

--- BEGIN FILE ---
```
### aiohttp/web.py
import abc
import asyncio
import binascii
import collections
import mimetypes
import cgi
import http.cookies
import io
import json
import re
import os
import warnings

from urllib.parse import urlsplit, parse_qsl, urlencode, unquote
from types import MappingProxyType

from . import hdrs
from .abc import AbstractRouter, AbstractMatchInfo
from .helpers import reify
from .log import web_logger
from .multidict import (CIMultiDictProxy,
                        CIMultiDict,
                        MultiDictProxy,
                        MultiDict)
from .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11
from .server import ServerHttpProtocol
from .streams import EOF_MARKER
from .websocket import do_handshake, MSG_BINARY, MSG_CLOSE, MSG_PING, MSG_TEXT
from .errors import HttpProcessingError, WSClientDisconnectedError


__all__ = [
    'WSClientDisconnectedError',
    'Application',
    'HttpVersion',
    'RequestHandler',
    'RequestHandlerFactory',
    'Request',
    'StreamResponse',
    'Response',
    'WebSocketResponse',
    'UrlDispatcher',
    'UrlMappingMatchInfo',
    'HTTPException',
    'HTTPError',
    'HTTPRedirection',
    'HTTPSuccessful',
    'HTTPOk',
    'HTTPCreated',
    'HTTPAccepted',
    'HTTPNonAuthoritativeInformation',
    'HTTPNoContent',
    'HTTPResetContent',
    'HTTPPartialContent',
    'HTTPMultipleChoices',
    'HTTPMovedPermanently',
    'HTTPFound',
    'HTTPSeeOther',
    'HTTPNotModified',
    'HTTPUseProxy',
    'HTTPTemporaryRedirect',
    'HTTPClientError',
    'HTTPBadRequest',
    'HTTPUnauthorized',
    'HTTPPaymentRequired',
    'HTTPForbidden',
    'HTTPNotFound',
    'HTTPMethodNotAllowed',
    'HTTPNotAcceptable',
    'HTTPProxyAuthenticationRequired',
    'HTTPRequestTimeout',
    'HTTPConflict',
    'HTTPGone',
    'HTTPLengthRequired',
    'HTTPPreconditionFailed',
    'HTTPRequestEntityTooLarge',
    'HTTPRequestURITooLong',
    'HTTPUnsupportedMediaType',
    'HTTPRequestRangeNotSatisfiable',
    'HTTPExpectationFailed',
    'HTTPServerError',
    'HTTPInternalServerError',
    'HTTPNotImplemented',
    'HTTPBadGateway',
    'HTTPServiceUnavailable',
    'HTTPGatewayTimeout',
    'HTTPVersionNotSupported',
]


sentinel = object()
retype = type(re.compile('@'))


class HeadersMixin:

    _content_type = None
    _content_dict = None
    _stored_content_type = sentinel

    def _parse_content_type(self, raw):
        self._stored_content_type = raw
        if raw is None:
            # default value according to RFC 2616
            self._content_type = 'application/octet-stream'
            self._content_dict = {}
        else:
            self._content_type, self._content_dict = cgi.parse_header(raw)

    @property
    def content_type(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):
        """The value of content part for Content-Type HTTP header."""
        raw = self.headers.get(_CONTENT_TYPE)
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_type

    @property
    def charset(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):
        """The value of charset part for Content-Type HTTP header."""
        raw = self.headers.get(_CONTENT_TYPE)
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_dict.get('charset')

    @property
    def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):
        """The value of Content-Length HTTP header."""
        l = self.headers.get(_CONTENT_LENGTH)
        if l is None:
            return None
        else:
            return int(l)


FileField = collections.namedtuple('Field', 'name filename file content_type')


############################################################
# HTTP Request
############################################################


class Request(dict, HeadersMixin):

    def __init__(self, app, message, payload, transport, reader, writer, *,
                 _HOST=hdrs.HOST):
        self._app = app
        self._version = message.version
        self._transport = transport
        self._reader = reader
        self._writer = writer
        self._method = message.method
        self._host = message.headers.get(_HOST)
        self._path_qs = message.path
        res = urlsplit(message.path)
        self._path = unquote(res.path)
        self._query_string = res.query
        self._post = None
        self._post_files_cache = None
        self._headers = CIMultiDictProxy(message.headers)

        if self._version < HttpVersion11:
            self._keep_alive = False
        elif message.should_close:
            self._keep_alive = False
        else:
            self._keep_alive = True

        # matchdict, route_name, handler
        # or information about traversal lookup
        self._match_info = None  # initialized after route resolving

        self._payload = payload
        self._cookies = None

        self._read_bytes = None

    @property
    def method(self):
        """Read only property for getting HTTP method.

        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.
        """
        return self._method

    @property
    def version(self):
        """Read only property for getting HTTP version of request.

        Returns aiohttp.protocol.HttpVersion instance.
        """
        return self._version

    @property
    def host(self):
        """Read only property for getting *HOST* header of request.

        Returns str or None if HTTP request has no HOST header.
        """
        return self._host

    @property
    def path_qs(self):
        """The URL including PATH_INFO and the query string.

        E.g, /app/blog?id=10
        """
        return self._path_qs

    @property
    def path(self):
        """The URL including *PATH INFO* without the host or scheme.

        E.g., ``/app/blog``
        """
        return self._path

    @property
    def query_string(self):
        """The query string in the URL.

        E.g., id=10
        """
        return self._query_string

    @reify
    def GET(self):
        """A multidict with all the variables in the query string.

        Lazy property.
        """
        return MultiDictProxy(MultiDict(parse_qsl(self._query_string)))

    @reify
    def POST(self):
        """A multidict with all the variables in the POST parameters.

        post() methods has to be called before using this attribute.
        """
        if self._post is None:
            raise RuntimeError("POST is not available before post()")
        return self._post

    @property
    def headers(self):
        """A case-insensitive multidict with all headers.

        Lazy property.
        """
        return self._headers

    @property
    def keep_alive(self):
        """Is keepalive enabled by client?"""
        return self._keep_alive

    @property
    def match_info(self):
        """Result of route resolving."""
        return self._match_info

    @property
    def app(self):
        """Application instance."""
        return self._app

    @property
    def transport(self):
        """Transport used for request processing."""
        return self._transport

    @property
    def cookies(self):
        """Return request cookies.

        A read-only dictionary-like object.
        """
        if self._cookies is None:
            raw = self.headers.get(hdrs.COOKIE, '')
            parsed = http.cookies.SimpleCookie(raw)
            self._cookies = MappingProxyType(
                {key: val.value for key, val in parsed.items()})
        return self._cookies

    @property
    def payload(self):
        """Return raw payload stream."""
        warnings.warn('use Request.content instead', DeprecationWarning)
        return self._payload

    @property
    def content(self):
        """Return raw payload stream."""
        return self._payload

    @asyncio.coroutine
    def release(self):
        """Release request.

        Eat unread part of HTTP BODY if present.
        """
        chunk = yield from self._payload.readany()
        while chunk is not EOF_MARKER or chunk:
            chunk = yield from self._payload.readany()

    @asyncio.coroutine
    def read(self):
        """Read request body if present.

        Returns bytes object with full request content.
        """
        if self._read_bytes is None:
            body = bytearray()
            while True:
                chunk = yield from self._payload.readany()
                body.extend(chunk)
                if chunk is EOF_MARKER:
                    break
            self._read_bytes = bytes(body)
        return self._read_bytes

    @asyncio.coroutine
    def text(self):
        """Return BODY as text using encoding from .charset."""
        bytes_body = yield from self.read()
        encoding = self.charset or 'utf-8'
        return bytes_body.decode(encoding)

    @asyncio.coroutine
    def json(self, *, loader=json.loads):
        """Return BODY as JSON."""
        body = yield from self.text()
        return loader(body)

    @asyncio.coroutine
    def post(self):
        """Return POST parameters."""
        if self._post is not None:
            return self._post
        if self.method not in ('POST', 'PUT', 'PATCH'):
            self._post = MultiDictProxy(MultiDict())
            return self._post

        content_type = self.content_type
        if (content_type not in ('',
                                 'application/x-www-form-urlencoded',
                                 'multipart/form-data')):
            self._post = MultiDictProxy(MultiDict())
            return self._post

        body = yield from self.read()
        content_charset = self.charset or 'utf-8'

        environ = {'REQUEST_METHOD': self.method,
                   'CONTENT_LENGTH': str(len(body)),
                   'QUERY_STRING': '',
                   'CONTENT_TYPE': self.headers.get(hdrs.CONTENT_TYPE)}

        fs = cgi.FieldStorage(fp=io.BytesIO(body),
                              environ=environ,
                              keep_blank_values=True,
                              encoding=content_charset)

        supported_transfer_encoding = {
            'base64': binascii.a2b_base64,
            'quoted-printable': binascii.a2b_qp
        }

        out = MultiDict()
        for field in fs.list or ():
            transfer_encoding = field.headers.get(
                'Content-Transfer-Encoding', None)
            if field.filename:
                ff = FileField(field.name,
                               field.filename,
                               field.file,  # N.B. file closed error
                               field.type)
                if self._post_files_cache is None:
                    self._post_files_cache = {}
                self._post_files_cache[field.name] = field
                out.add(field.name, ff)
            else:
                value = field.value
                if transfer_encoding in supported_transfer_encoding:
                    # binascii accepts bytes
                    value = value.encode('utf-8')
                    value = supported_transfer_encoding[
                        transfer_encoding](value)
                out.add(field.name, value)

        self._post = MultiDictProxy(out)
        return self._post

    def __repr__(self):
        return "<{} {} {} >".format(self.__class__.__name__,
                                    self.method, self.path)


############################################################
# HTTP Response classes
############################################################


class StreamResponse(HeadersMixin):

    def __init__(self, *, status=200, reason=None):
        self._body = None
        self._keep_alive = None
        self._chunked = False
        self._chunk_size = None
        self._compression = False
        self._compression_force = False
        self._headers = CIMultiDict()
        self._cookies = http.cookies.SimpleCookie()
        self.set_status(status, reason)

        self._req = None
        self._resp_impl = None
        self._eof_sent = False

    def _copy_cookies(self):
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            self.headers.add(hdrs.SET_COOKIE, value)

    @property
    def started(self):
        return self._resp_impl is not None

    @property
    def status(self):
        return self._status

    @property
    def chunked(self):
        return self._chunked

    @property
    def compression(self):
        return self._compression

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None):
        self._status = int(status)
        if reason is None:
            reason = ResponseImpl.calc_reason(status)
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    def enable_chunked_encoding(self, chunk_size=None):
        """Enables automatic chunked transfer encoding."""
        self._chunked = True
        self._chunk_size = chunk_size

    def enable_compression(self, force=False):
        """Enables response compression with `deflate` encoding."""
        self._compression = True
        self._compression_force = force

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self, name, value, *, expires=None,
                   domain=None, max_age=None, path='/',
                   secure=None, httponly=None, version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]
        if expires is not None:
            c['expires'] = expires
        if domain is not None:
            c['domain'] = domain
        if max_age is not None:
            c['max-age'] = max_age
        if path is not None:
            c['path'] = path
        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path='/'):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name, '', max_age=0, domain=domain, path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            # TODO: raise error if chunked enabled
            self.headers[hdrs.CONTENT_LENGTH] = str(value)
        elif hdrs.CONTENT_LENGTH in self.headers:
            del self.headers[hdrs.CONTENT_LENGTH]

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value).lower()
        self._generate_content_type_header()

    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers[CONTENT_TYPE] = ctype

    def _start_pre_check(self, request):
        if self._resp_impl is not None:
            if self._req is not request:
                raise RuntimeError(
                    'Response has been started with different request.')
            else:
                return self._resp_impl
        else:
            return None

    def start(self, request):
        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl

        self._req = request
        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive

        resp_impl = self._resp_impl = ResponseImpl(
            request._writer,
            self._status,
            request.version,
            not keep_alive,
            self._reason)

        self._copy_cookies()

        if self._compression:
            if (self._compression_force or
                    'deflate' in request.headers.get(
                        hdrs.ACCEPT_ENCODING, '')):
                resp_impl.add_compression_filter()

        if self._chunked:
            resp_impl.enable_chunked_encoding()
            if self._chunk_size:
                resp_impl.add_chunking_filter(self._chunk_size)

        headers = self.headers.items()
        for key, val in headers:
            resp_impl.add_header(key, val)

        resp_impl.send_headers()
        return resp_impl

    def write(self, data):
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError('data argument must be byte-ish (%r)' %
                            type(data))

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._resp_impl is None:
            raise RuntimeError("Cannot call write() before start()")

        if data:
            return self._resp_impl.write(data)
        else:
            return ()

    @asyncio.coroutine
    def drain(self):
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")
        yield from self._resp_impl.transport.drain()

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self._resp_impl.write_eof()
        self._eof_sent = True

    def __repr__(self):
        if self.started:
            info = "{} {} ".format(self._req.method, self._req.path)
        else:
            info = "not started"
        return "<{} {} {}>".format(self.__class__.__name__,
                                   self.reason, info)


class Response(StreamResponse):

    def __init__(self, *, body=None, status=200,
                 reason=None, headers=None,
                 text=None, content_type=None):
        super().__init__(status=status, reason=reason)

        if headers is not None:
            self.headers.extend(headers)

        if body is not None and text is not None:
            raise ValueError("body and text are not allowed together.")

        if text is not None:
            if hdrs.CONTENT_TYPE not in self.headers:
                # fast path for filling headers
                if not isinstance(text, str):
                    raise TypeError('text argument must be str (%r)' %
                                    type(text))
                if content_type is None:
                    content_type = 'text/plain'
                self.headers[hdrs.CONTENT_TYPE] = (
                    content_type + '; charset=utf-8')
                self._content_type = content_type
                self._content_dict = {'charset': 'utf-8'}
                self.body = text.encode('utf-8')
            else:
                self.text = text
        else:
            if content_type:
                self.content_type = content_type
            if body is not None:
                self.body = body
            else:
                self.body = None

    @property
    def body(self):
        return self._body

    @body.setter
    def body(self, body):
        if body is not None and not isinstance(body, bytes):
            raise TypeError('body argument must be bytes (%r)' % type(body))
        self._body = body
        if body is not None:
            self.content_length = len(body)
        else:
            self.content_length = 0

    @property
    def text(self):
        return self._body.decode(self.charset or 'utf-8')

    @text.setter
    def text(self, text):
        if text is not None and not isinstance(text, str):
            raise TypeError('text argument must be str (%r)' % type(text))

        if self.content_type == 'application/octet-stream':
            self.content_type = 'text/plain'
        if self.charset is None:
            self.charset = 'utf-8'

        self.body = text.encode(self.charset)

    @asyncio.coroutine
    def write_eof(self):
        body = self._body
        if body is not None:
            self.write(body)
        yield from super().write_eof()


class WebSocketResponse(StreamResponse):

    def __init__(self, *, protocols=()):
        super().__init__(status=101)
        self._protocols = protocols
        self._protocol = None
        self._writer = None
        self._reader = None
        self._closing = False
        self._loop = None
        self._closing_fut = None

    def start(self, request):
        # make pre-check to don't hide it by do_handshake() exceptions
        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl

        try:
            status, headers, parser, writer, protocol = do_handshake(
                request.method, request.headers, request.transport,
                self._protocols)
        except HttpProcessingError as err:
            if err.code == 405:
                raise HTTPMethodNotAllowed(request.method, ['GET'])
            elif err.code == 400:
                raise HTTPBadRequest(text=err.message, headers=err.headers)
            else:  # pragma: no cover
                raise HTTPInternalServerError() from err

        if self.status != status:
            self.set_status(status)
        for k, v in headers:
            self.headers[k] = v
        self.force_close()

        resp_impl = super().start(request)

        self._reader = request._reader.set_parser(parser)
        self._writer = writer
        self._protocol = protocol
        self._loop = request.app.loop
        self._closing_fut = asyncio.Future(loop=self._loop)

        return resp_impl

    def can_start(self, request):
        if self._writer is not None:
            raise RuntimeError('Already started')
        try:
            _, _, _, _, protocol = do_handshake(
                request.method, request.headers, request.transport,
                self._protocols)
        except HttpProcessingError:
            return False, None
        else:
            return True, protocol

    @property
    def closing(self):
        return self._closing

    @property
    def protocol(self):
        return self._protocol

    def ping(self, message='b'):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        self._writer.ping(message)

    def pong(self, message='b'):
        # unsolicited pong
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        self._writer.pong(message)

    def send_str(self, data):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        if not isinstance(data, str):
            raise TypeError('data argument must be str (%r)' % type(data))
        self._writer.send(data, binary=False)

    def send_bytes(self, data):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError('data argument must be byte-ish (%r)' %
                            type(data))
        self._writer.send(data, binary=True)

    def close(self, *, code=1000, message=b''):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if not self._closing:
            self._closing = True
            self._writer.close(code, message)
        else:
            raise RuntimeError('Already closing')

    @asyncio.coroutine
    def wait_closed(self):
        if self._closing_fut is None:
            raise RuntimeError('Call .start() first')
        yield from self._closing_fut

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self.wait_closed()
        self._eof_sent = True

    @asyncio.coroutine
    def receive_msg(self):
        if self._reader is None:
            raise RuntimeError('Call .start() first')
        while True:
            try:
                msg = yield from self._reader.read()
            except Exception as exc:
                self._closing_fut.set_exception(exc)
                raise

            if msg.tp == MSG_CLOSE:
                if self._closing:
                    exc = WSClientDisconnectedError(msg.data, msg.extra)
                    self._closing_fut.set_exception(exc)
                    raise exc
                else:
                    self._closing = True
                    self._writer.close(msg.data, msg.extra)
                    yield from self.drain()
                    exc = WSClientDisconnectedError(msg.data, msg.extra)
                    self._closing_fut.set_exception(exc)
                    raise exc
            elif not self._closing:
                if msg.tp == MSG_PING:
                    self._writer.pong(msg.data)
                elif msg.tp in (MSG_TEXT, MSG_BINARY):
                    return msg

    @asyncio.coroutine
    def receive_str(self):
        msg = yield from self.receive_msg()
        if msg.tp != MSG_TEXT:
            raise TypeError(
                "Received message {}:{!r} is not str".format(msg.tp, msg.data))
        return msg.data

    @asyncio.coroutine
    def receive_bytes(self):
        msg = yield from self.receive_msg()
        if msg.tp != MSG_BINARY:
            raise TypeError(
                "Received message {}:{!r} is not bytes".format(msg.tp,
                                                               msg.data))
        return msg.data

    def write(self, data):
        raise RuntimeError("Cannot call .write() for websocket")


############################################################
# HTTP Exceptions
############################################################

class HTTPException(Response, Exception):

    # You should set in subclasses:
    # status = 200

    status_code = None

    def __init__(self, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        Response.__init__(self, status=self.status_code,
                          headers=headers, reason=reason,
                          body=body, text=text, content_type=content_type)
        Exception.__init__(self, self.reason)
        if self.body is None:
            self.text = "{}: {}".format(self.status, self.reason)


class HTTPError(HTTPException):
    """Base class for exceptions with status codes in the 400s and 500s."""


class HTTPRedirection(HTTPException):
    """Base class for exceptions with status codes in the 300s."""


class HTTPSuccessful(HTTPException):
    """Base class for exceptions with status codes in the 200s."""


class HTTPOk(HTTPSuccessful):
    status_code = 200


class HTTPCreated(HTTPSuccessful):
    status_code = 201


class HTTPAccepted(HTTPSuccessful):
    status_code = 202


class HTTPNonAuthoritativeInformation(HTTPSuccessful):
    status_code = 203


class HTTPNoContent(HTTPSuccessful):
    status_code = 204


class HTTPResetContent(HTTPSuccessful):
    status_code = 205


class HTTPPartialContent(HTTPSuccessful):
    status_code = 206


############################################################
# 3xx redirection
############################################################


class _HTTPMove(HTTPRedirection):

    def __init__(self, location, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        if not location:
            raise ValueError("HTTP redirects need a location to redirect to.")
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Location'] = location
        self.location = location


class HTTPMultipleChoices(_HTTPMove):
    status_code = 300


class HTTPMovedPermanently(_HTTPMove):
    status_code = 301


class HTTPFound(_HTTPMove):
    status_code = 302


# This one is safe after a POST (the redirected location will be
# retrieved with GET):
class HTTPSeeOther(_HTTPMove):
    status_code = 303


class HTTPNotModified(HTTPRedirection):
    # FIXME: this should include a date or etag header
    status_code = 304


class HTTPUseProxy(_HTTPMove):
    # Not a move, but looks a little like one
    status_code = 305


class HTTPTemporaryRedirect(_HTTPMove):
    status_code = 307


############################################################
# 4xx client error
############################################################


class HTTPClientError(HTTPError):
    pass


class HTTPBadRequest(HTTPClientError):
    status_code = 400


class HTTPUnauthorized(HTTPClientError):
    status_code = 401


class HTTPPaymentRequired(HTTPClientError):
    status_code = 402


class HTTPForbidden(HTTPClientError):
    status_code = 403


class HTTPNotFound(HTTPClientError):
    status_code = 404


class HTTPMethodNotAllowed(HTTPClientError):
    status_code = 405

    def __init__(self, method, allowed_methods, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        allow = ','.join(sorted(allowed_methods))
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Allow'] = allow
        self.allowed_methods = allowed_methods
        self.method = method.upper()


class HTTPNotAcceptable(HTTPClientError):
    status_code = 406


class HTTPProxyAuthenticationRequired(HTTPClientError):
    status_code = 407


class HTTPRequestTimeout(HTTPClientError):
    status_code = 408


class HTTPConflict(HTTPClientError):
    status_code = 409


class HTTPGone(HTTPClientError):
    status_code = 410


class HTTPLengthRequired(HTTPClientError):
    status_code = 411


class HTTPPreconditionFailed(HTTPClientError):
    status_code = 412


class HTTPRequestEntityTooLarge(HTTPClientError):
    status_code = 413


class HTTPRequestURITooLong(HTTPClientError):
    status_code = 414


class HTTPUnsupportedMediaType(HTTPClientError):
    status_code = 415


class HTTPRequestRangeNotSatisfiable(HTTPClientError):
    status_code = 416


class HTTPExpectationFailed(HTTPClientError):
    status_code = 417


############################################################
# 5xx Server Error
############################################################
#  Response status codes beginning with the digit "5" indicate cases in
#  which the server is aware that it has erred or is incapable of
#  performing the request. Except when responding to a HEAD request, the
#  server SHOULD include an entity containing an explanation of the error
#  situation, and whether it is a temporary or permanent condition. User
#  agents SHOULD display any included entity to the user. These response
#  codes are applicable to any request method.


class HTTPServerError(HTTPError):
    pass


class HTTPInternalServerError(HTTPServerError):
    status_code = 500


class HTTPNotImplemented(HTTPServerError):
    status_code = 501


class HTTPBadGateway(HTTPServerError):
    status_code = 502


class HTTPServiceUnavailable(HTTPServerError):
    status_code = 503


class HTTPGatewayTimeout(HTTPServerError):
    status_code = 504


class HTTPVersionNotSupported(HTTPServerError):
    status_code = 505


############################################################
# UrlDispatcher implementation
############################################################


class UrlMappingMatchInfo(dict, AbstractMatchInfo):

    def __init__(self, match_dict, route):
        super().__init__(match_dict)
        self._route = route

    @property
    def handler(self):
        return self._route.handler

    @property
    def route(self):
        return self._route

    def __repr__(self):
        return "<MatchInfo {}: {}>".format(super().__repr__(), self._route)


@asyncio.coroutine
def defaultExpectHandler(request):
    """Default handler for Except: 100-continue"""
    if request.version == HttpVersion11:
        request.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")


class Route(metaclass=abc.ABCMeta):

    def __init__(self, method, handler, name, expect_handler=None):
        if expect_handler is None:
            expect_handler = defaultExpectHandler
        assert asyncio.iscoroutinefunction(expect_handler), \
            'Coroutine is expected, got {!r}'.format(expect_handler)

        self._method = method
        self._handler = handler
        self._name = name
        self._expect_handler = expect_handler

    @property
    def method(self):
        return self._method

    @property
    def handler(self):
        return self._handler

    @property
    def name(self):
        return self._name

    @abc.abstractmethod
    def match(self, path):
        """Return dict with info for given path or
        None if route cannot process path."""

    @abc.abstractmethod
    def url(self, **kwargs):
        """Construct url for route with additional params."""

    @asyncio.coroutine
    def handle_expect_header(self, request):
        return (yield from self._expect_handler(request))

    @staticmethod
    def _append_query(url, query):
        if query is not None:
            return url + "?" + urlencode(query)
        else:
            return url


class PlainRoute(Route):

    def __init__(self, method, handler, name, path, expect_handler=None):
        super().__init__(method, handler, name, expect_handler=expect_handler)
        self._path = path

    def match(self, path):
        # string comparison is about 10 times faster than regexp matching
        if self._path == path:
            return {}
        else:
            return None

    def url(self, *, query=None):
        return self._append_query(self._path, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<PlainRoute {name}[{method}] {path} -> {handler!r}".format(
            name=name, method=self.method, path=self._path,
            handler=self.handler)


class DynamicRoute(Route):

    def __init__(self, method, handler, name, pattern, formatter,
                 expect_handler=None):
        super().__init__(method, handler, name, expect_handler=expect_handler)
        self._pattern = pattern
        self._formatter = formatter

    def match(self, path):
        match = self._pattern.match(path)
        if match is None:
            return None
        else:
            return match.groupdict()

    def url(self, *, parts, query=None):
        url = self._formatter.format_map(parts)
        return self._append_query(url, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return ("<DynamicRoute {name}[{method}] {formatter} -> {handler!r}"
                .format(name=name, method=self.method,
                        formatter=self._formatter, handler=self.handler))


class StaticRoute(Route):

    limit = 8192

    def __init__(self, name, prefix, directory, expect_handler=None):
        assert prefix.startswith('/'), prefix
        assert prefix.endswith('/'), prefix
        super().__init__(
            'GET', self.handle, name, expect_handler=expect_handler)
        self._prefix = prefix
        self._prefix_len = len(self._prefix)
        self._directory = directory

    def match(self, path):
        if not path.startswith(self._prefix):
            return None
        return {'filename': path[self._prefix_len:]}

    def url(self, *, filename, query=None):
        while filename.startswith('/'):
            filename = filename[1:]
        url = self._prefix + filename
        return self._append_query(url, query)

    @asyncio.coroutine
    def handle(self, request):
        resp = StreamResponse()
        filename = request.match_info['filename']
        filepath = os.path.join(self._directory, filename)
        if '..' in filename:
            raise HTTPNotFound()
        if not os.path.exists(filepath) or not os.path.isfile(filepath):
            raise HTTPNotFound()

        ct = mimetypes.guess_type(filename)[0]
        if not ct:
            ct = 'application/octet-stream'
        resp.content_type = ct

        file_size = os.stat(filepath).st_size
        single_chunk = file_size < self.limit

        if single_chunk:
            resp.content_length = file_size
        resp.start(request)

        with open(filepath, 'rb') as f:
            chunk = f.read(self.limit)
            if single_chunk:
                resp.write(chunk)
            else:
                while chunk:
                    resp.write(chunk)
                    chunk = f.read(self.limit)

        return resp

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<StaticRoute {name}[{method}] {path} -> {directory!r}".format(
            name=name, method=self.method, path=self._prefix,
            directory=self._directory)


class _NotFoundMatchInfo(UrlMappingMatchInfo):

    def __init__(self):
        super().__init__({}, None)

    @property
    def handler(self):
        return self._not_found

    @property
    def route(self):
        return None

    @asyncio.coroutine
    def _not_found(self, request):
        raise HTTPNotFound()

    def __repr__(self):
        return "<MatchInfo: not found>"


class _MethodNotAllowedMatchInfo(UrlMappingMatchInfo):

    def __init__(self, method, allowed_methods):
        super().__init__({}, None)
        self._method = method
        self._allowed_methods = allowed_methods

    @property
    def handler(self):
        return self._not_allowed

    @property
    def route(self):
        return None

    @asyncio.coroutine
    def _not_allowed(self, request):
        raise HTTPMethodNotAllowed(self._method, self._allowed_methods)

    def __repr__(self):
        return ("<MatchInfo: method {} is not allowed (allowed methods: {}>"
                .format(self._method,
                        ', '.join(sorted(self._allowed_methods))))


class UrlDispatcher(AbstractRouter, collections.abc.Mapping):

    DYN = re.compile(r'^\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\}$')
    DYN_WITH_RE = re.compile(
        r'^\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\}$')
    GOOD = r'[^{}/]+'
    PLAIN = re.compile('^' + GOOD + '$')

    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}

    def __init__(self):
        super().__init__()
        self._urls = []
        self._routes = {}

    @asyncio.coroutine
    def resolve(self, request):
        path = request.path
        method = request.method
        allowed_methods = set()
        for route in self._urls:
            match_dict = route.match(path)
            if match_dict is None:
                continue
            route_method = route.method
            if route_method != method:
                allowed_methods.add(route_method)
            else:
                return UrlMappingMatchInfo(match_dict, route)
        else:
            if allowed_methods:
                return _MethodNotAllowedMatchInfo(method, allowed_methods)
            else:
                return _NotFoundMatchInfo()

    def __iter__(self):
        return iter(self._routes)

    def __len__(self):
        return len(self._routes)

    def __contains__(self, name):
        return name in self._routes

    def __getitem__(self, name):
        return self._routes[name]

    def _register_endpoint(self, route):
        name = route.name
        if name is not None:
            if name in self._routes:
                raise ValueError('Duplicate {!r}, '
                                 'already handled by {!r}'
                                 .format(name, self._routes[name]))
            else:
                self._routes[name] = route
        self._urls.append(route)

    def add_route(self, method, path, handler,
                  *, name=None, expect_handler=None):

        assert callable(handler), handler
        if not asyncio.iscoroutinefunction(handler):
            handler = asyncio.coroutine(handler)
        method = method.upper()
        assert method in self.METHODS, method

        if isinstance(path, retype):
            route = DynamicRoute(
                method, handler, name, path,
                path.pattern, expect_handler=expect_handler)

            self._register_endpoint(route)
            return route

        assert path.startswith('/')
        parts = []
        factory = PlainRoute
        format_parts = []
        for part in path.split('/'):
            if not part:
                continue
            match = self.DYN.match(part)
            if match:
                parts.append('(?P<' + match.group('var') + '>' +
                             self.GOOD + ')')
                factory = DynamicRoute
                format_parts.append('{'+match.group('var')+'}')
                continue

            match = self.DYN_WITH_RE.match(part)
            if match:
                parts.append('(?P<' + match.group('var') + '>' +
                             match.group('re') + ')')
                factory = DynamicRoute
                format_parts.append('{'+match.group('var')+'}')
                continue
            if self.PLAIN.match(part):
                parts.append(re.escape(part))
                format_parts.append(part)
                continue
            raise ValueError("Invalid path '{}'['{}']".format(path, part))
        if factory is PlainRoute:
            route = PlainRoute(
                method, handler, name, path, expect_handler=expect_handler)
        else:
            pattern = '/' + '/'.join(parts)
            formatter = '/' + '/'.join(format_parts)
            if path.endswith('/') and pattern != '/':
                pattern += '/'
                formatter += '/'
            try:
                compiled = re.compile('^' + pattern + '$')
            except re.error as exc:
                raise ValueError(
                    "Bad pattern '{}': {}".format(pattern, exc)) from None
            route = DynamicRoute(
                method, handler, name, compiled,
                formatter, expect_handler=expect_handler)

        self._register_endpoint(route)
        return route

    def add_static(self, prefix, path, *, name=None, expect_handler=None):
        """
        Adds static files view
        :param prefix - url prefix
        :param path - folder with files
        """
        assert prefix.startswith('/')
        assert os.path.isdir(path), 'Path does not directory %s' % path
        path = os.path.abspath(path)
        if not prefix.endswith('/'):
            prefix += '/'
        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)
        self._register_endpoint(route)
        return route


############################################################
# Application implementation
############################################################


class RequestHandler(ServerHttpProtocol):

    def __init__(self, manager, app, router, **kwargs):
        super().__init__(**kwargs)

        self._manager = manager
        self._app = app
        self._router = router
        self._middlewares = app.middlewares

    def connection_made(self, transport):
        super().connection_made(transport)

        self._manager.connection_made(self, transport)

    def connection_lost(self, exc):
        self._manager.connection_lost(self, exc)

        super().connection_lost(exc)

    @asyncio.coroutine
    def handle_request(self, message, payload):
        now = self._loop.time()

        app = self._app
        request = Request(app, message, payload,
                          self.transport, self.reader, self.writer)
        try:
            match_info = yield from self._router.resolve(request)

            assert isinstance(match_info, AbstractMatchInfo), match_info

            resp = None
            request._match_info = match_info
            if request.headers.get(hdrs.EXPECT, '').lower() == "100-continue":
                resp = (
                    yield from match_info.route.handle_expect_header(request))

            if resp is None:
                handler = match_info.handler
                for factory in reversed(self._middlewares):
                    handler = yield from factory(app, handler)
                resp = yield from handler(request)

            if not isinstance(resp, StreamResponse):
                raise RuntimeError(
                    ("Handler {!r} should return response instance, "
                     "got {!r} [middlewares {!r}]").format(
                         match_info.handler,
                         type(resp),
                         self._middlewares))
        except HTTPException as exc:
            resp = exc

        resp_msg = resp.start(request)
        yield from resp.write_eof()

        # notify server about keep-alive
        self.keep_alive(resp_msg.keep_alive())

        # log access
        self.log_access(message, None, resp_msg, self._loop.time() - now)


class RequestHandlerFactory:

    def __init__(self, app, router, *,
                 handler=RequestHandler, loop=None, **kwargs):
        self._app = app
        self._router = router
        self._handler = handler
        self._loop = loop
        self._connections = {}
        self._kwargs = kwargs
        self._kwargs.setdefault('logger', app.logger)

    @property
    def connections(self):
        return list(self._connections.keys())

    def connection_made(self, handler, transport):
        self._connections[handler] = transport

    def connection_lost(self, handler, exc=None):
        if handler in self._connections:
            del self._connections[handler]

    @asyncio.coroutine
    def finish_connections(self, timeout=None):
        for handler in self._connections.keys():
            handler.closing()

        @asyncio.coroutine
        def cleanup():
            sleep = 0.05
            while self._connections:
                yield from asyncio.sleep(sleep, loop=self._loop)
                if sleep < 5:
                    sleep = sleep * 2

        if timeout:
            try:
                yield from asyncio.wait_for(
                    cleanup(), timeout, loop=self._loop)
            except asyncio.TimeoutError:
                self._app.logger.warning(
                    "Not all connections are closed (pending: %d)",
                    len(self._connections))

        for transport in self._connections.values():
            transport.close()

        self._connections.clear()

    def __call__(self):
        return self._handler(
            self, self._app, self._router, loop=self._loop, **self._kwargs)


class Application(dict):

    def __init__(self, *, logger=web_logger, loop=None,
                 router=None, handler_factory=RequestHandlerFactory,
                 middlewares=()):
        if loop is None:
            loop = asyncio.get_event_loop()
        if router is None:
            router = UrlDispatcher()
        assert isinstance(router, AbstractRouter), router

        self._router = router
        self._handler_factory = handler_factory
        self._finish_callbacks = []
        self._loop = loop
        self.logger = logger

        for factory in middlewares:
            assert asyncio.iscoroutinefunction(factory), factory
        self._middlewares = tuple(middlewares)

    @property
    def router(self):
        return self._router

    @property
    def loop(self):
        return self._loop

    @property
    def middlewares(self):
        return self._middlewares

    def make_handler(self, **kwargs):
        return self._handler_factory(
            self, self.router, loop=self.loop, **kwargs)

    @asyncio.coroutine
    def finish(self):
        callbacks = self._finish_callbacks
        self._finish_callbacks = []

        for (cb, args, kwargs) in callbacks:
            try:
                res = cb(self, *args, **kwargs)
                if (asyncio.iscoroutine(res) or
                        isinstance(res, asyncio.Future)):
                    yield from res
            except Exception as exc:
                self._loop.call_exception_handler({
                    'message': "Error in finish callback",
                    'exception': exc,
                    'application': self,
                })

    def register_on_finish(self, func, *args, **kwargs):
        self._finish_callbacks.insert(0, (func, args, kwargs))

    def __call__(self):
        """gunicorn compatibility"""
        return self

    def __repr__(self):
        return "<Application>"

```
--- 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:** Adding Route with Regex and Optional Trailing Slash Raises ValueError

**Description:**
When attempting to add a route that includes a regular expression with an optional trailing slash, the URL dispatcher raises a `ValueError`. This prevents the route from being registered correctly, disrupting the application's routing functionality.

**Example Code:**
```python
handler = make_handler()
router.add_route('GET', '/handler/{to:[^/]+/?}', handler)
```

**Error Message:**
```
ValueError: Invalid path '/handler/{to:[^/]+/?}'['{to:[^']
```

**Expected Behavior:**
The route should be added successfully, allowing URLs that match the pattern with or without a trailing slash. For example, both `/handler/1234` and `/handler/1234/` should be valid and correctly routed to the specified handler.

**Actual Behavior:**
Adding the route raises a `ValueError`, indicating that the provided path is invalid. This error prevents the route from being registered, causing any matching URLs to fail to resolve correctly.

[/ISSUE]
{
    "TestUrlDispatcher.test_add_invalid_path": "PASSED",
    "TestUrlDispatcher.test_add_route_root": "PASSED",
    "TestUrlDispatcher.test_add_route_simple": "PASSED",
    "TestUrlDispatcher.test_add_route_with_re": "PASSED",
    "TestUrlDispatcher.test_add_route_with_re_and_slashes": "PASSED",
    "TestUrlDispatcher.test_add_route_with_re_including_slashes": "PASSED",
    "TestUrlDispatcher.test_add_route_with_re_not_match": "PASSED",
    "TestUrlDispatcher.test_add_static": "PASSED",
    "TestUrlDispatcher.test_add_url_escaping": "PASSED",
    "TestUrlDispatcher.test_add_url_invalid1": "PASSED",
    "TestUrlDispatcher.test_add_url_invalid2": "PASSED",
    "TestUrlDispatcher.test_add_url_invalid3": "PASSED",
    "TestUrlDispatcher.test_add_url_invalid4": "PASSED",
    "TestUrlDispatcher.test_add_with_matchdict": "PASSED",
    "TestUrlDispatcher.test_add_with_name": "PASSED",
    "TestUrlDispatcher.test_add_with_tailing_slash": "PASSED",
    "TestUrlDispatcher.test_contains": "PASSED",
    "TestUrlDispatcher.test_custom_expect_handler_dynamic": "PASSED",
    "TestUrlDispatcher.test_custom_expect_handler_plain": "PASSED",
    "TestUrlDispatcher.test_default_expect_handler": "PASSED",
    "TestUrlDispatcher.test_double_add_url_with_the_same_name": "PASSED",
    "TestUrlDispatcher.test_dynamic_not_match": "PASSED",
    "TestUrlDispatcher.test_dynamic_repr": "PASSED",
    "TestUrlDispatcher.test_dynamic_with_trailing_slash": "PASSED",
    "TestUrlDispatcher.test_expect_handler_non_coroutine": "PASSED",
    "TestUrlDispatcher.test_iter": "PASSED",
    "TestUrlDispatcher.test_len": "PASSED",
    "TestUrlDispatcher.test_match_second_result_in_table": "PASSED",
    "TestUrlDispatcher.test_not_allowed_repr": "PASSED",
    "TestUrlDispatcher.test_not_found_repr": "PASSED",
    "TestUrlDispatcher.test_plain_not_match": "PASSED",
    "TestUrlDispatcher.test_plain_repr": "PASSED",
    "TestUrlDispatcher.test_raise_method_not_allowed": "PASSED",
    "TestUrlDispatcher.test_raise_method_not_found": "PASSED",
    "TestUrlDispatcher.test_regular_match_info": "PASSED",
    "TestUrlDispatcher.test_route_dynamic": "PASSED",
    "TestUrlDispatcher.test_route_dynamic_with_regex": "PASSED",
    "TestUrlDispatcher.test_route_dynamic_with_regex_spec": "PASSED",
    "TestUrlDispatcher.test_route_dynamic_with_regex_spec_and_trailing_slash": "PASSED",
    "TestUrlDispatcher.test_route_plain": "PASSED",
    "TestUrlDispatcher.test_route_unknown_route_name": "PASSED",
    "TestUrlDispatcher.test_route_with_qs": "PASSED",
    "TestUrlDispatcher.test_static_adds_slash": "PASSED",
    "TestUrlDispatcher.test_static_dont_add_trailing_slash": "PASSED",
    "TestUrlDispatcher.test_static_not_match": "PASSED",
    "TestUrlDispatcher.test_static_repr": "PASSED",
    "TestUrlDispatcher.test_add_route_with_invalid_re": "FAILED"
}
diff --git a/aiohttp/web.py b/aiohttp/web.py
index fe38569c..bb524284 100644
--- a/aiohttp/web.py
+++ b/aiohttp/web.py
@@ -1383,7 +1383,7 @@ class UrlDispatcher(AbstractRouter, collections.abc.Mapping):
     DYN_WITH_RE = re.compile(
         r'^\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\}$')
     GOOD = r'[^{}/]+'
-    PLAIN = re.compile('^' + GOOD + '$')
+    ROUTE_RE = re.compile(r'(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})')
 
     METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}
 
@@ -1444,59 +1444,41 @@ class UrlDispatcher(AbstractRouter, collections.abc.Mapping):
         method = method.upper()
         assert method in self.METHODS, method
 
-        if isinstance(path, retype):
-            route = DynamicRoute(
-                method, handler, name, path,
-                path.pattern, expect_handler=expect_handler)
-
+        if not ('{' in path or '}' in path or self.ROUTE_RE.search(path)):
+            route = PlainRoute(
+                method, handler, name, path, expect_handler=expect_handler)
             self._register_endpoint(route)
             return route
 
-        assert path.startswith('/')
-        parts = []
-        factory = PlainRoute
-        format_parts = []
-        for part in path.split('/'):
-            if not part:
-                continue
+        pattern = ''
+        formatter = ''
+        for part in self.ROUTE_RE.split(path):
             match = self.DYN.match(part)
             if match:
-                parts.append('(?P<' + match.group('var') + '>' +
-                             self.GOOD + ')')
-                factory = DynamicRoute
-                format_parts.append('{'+match.group('var')+'}')
+                pattern += '(?P<' + match.group('var') + '>' + self.GOOD + ')'
+                formatter += '{%s}' % match.group('var')
                 continue
 
             match = self.DYN_WITH_RE.match(part)
             if match:
-                parts.append('(?P<' + match.group('var') + '>' +
-                             match.group('re') + ')')
-                factory = DynamicRoute
-                format_parts.append('{'+match.group('var')+'}')
-                continue
-            if self.PLAIN.match(part):
-                parts.append(re.escape(part))
-                format_parts.append(part)
+                pattern += '(?P<%(var)s>%(re)s)' % match.groupdict()
+                formatter += '{%s}' % match.group('var')
                 continue
-            raise ValueError("Invalid path '{}'['{}']".format(path, part))
-        if factory is PlainRoute:
-            route = PlainRoute(
-                method, handler, name, path, expect_handler=expect_handler)
-        else:
-            pattern = '/' + '/'.join(parts)
-            formatter = '/' + '/'.join(format_parts)
-            if path.endswith('/') and pattern != '/':
-                pattern += '/'
-                formatter += '/'
-            try:
-                compiled = re.compile('^' + pattern + '$')
-            except re.error as exc:
-                raise ValueError(
-                    "Bad pattern '{}': {}".format(pattern, exc)) from None
-            route = DynamicRoute(
-                method, handler, name, compiled,
-                formatter, expect_handler=expect_handler)
 
+            if '{' in part or '}' in part:
+                raise ValueError("Invalid path '{}'['{}']".format(path, part))
+
+            formatter += part
+            pattern += re.escape(part)
+
+        try:
+            compiled = re.compile('^' + pattern + '$')
+        except re.error as exc:
+            raise ValueError(
+                "Bad pattern '{}': {}".format(pattern, exc)) from None
+        route = DynamicRoute(
+            method, handler, name, compiled,
+            formatter, expect_handler=expect_handler)
         self._register_endpoint(route)
         return route
 

{"aiohttp/web.py": "import abc\nimport asyncio\nimport binascii\nimport collections\nimport mimetypes\nimport cgi\nimport http.cookies\nimport io\nimport json\nimport re\nimport os\nimport warnings\n\nfrom urllib.parse import urlsplit, parse_qsl, urlencode, unquote\nfrom types import MappingProxyType\n\nfrom . import hdrs\nfrom .abc import AbstractRouter, AbstractMatchInfo\nfrom .helpers import reify\nfrom .log import web_logger\nfrom .multidict import (CIMultiDictProxy,\n                        CIMultiDict,\n                        MultiDictProxy,\n                        MultiDict)\nfrom .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11\nfrom .server import ServerHttpProtocol\nfrom .streams import EOF_MARKER\nfrom .websocket import do_handshake, MSG_BINARY, MSG_CLOSE, MSG_PING, MSG_TEXT\nfrom .errors import HttpProcessingError, WSClientDisconnectedError\n\n\n__all__ = [\n    'WSClientDisconnectedError',\n    'Application',\n    'HttpVersion',\n    'RequestHandler',\n    'RequestHandlerFactory',\n    'Request',\n    'StreamResponse',\n    'Response',\n    'WebSocketResponse',\n    'UrlDispatcher',\n    'UrlMappingMatchInfo',\n    'HTTPException',\n    'HTTPError',\n    'HTTPRedirection',\n    'HTTPSuccessful',\n    'HTTPOk',\n    'HTTPCreated',\n    'HTTPAccepted',\n    'HTTPNonAuthoritativeInformation',\n    'HTTPNoContent',\n    'HTTPResetContent',\n    'HTTPPartialContent',\n    'HTTPMultipleChoices',\n    'HTTPMovedPermanently',\n    'HTTPFound',\n    'HTTPSeeOther',\n    'HTTPNotModified',\n    'HTTPUseProxy',\n    'HTTPTemporaryRedirect',\n    'HTTPClientError',\n    'HTTPBadRequest',\n    'HTTPUnauthorized',\n    'HTTPPaymentRequired',\n    'HTTPForbidden',\n    'HTTPNotFound',\n    'HTTPMethodNotAllowed',\n    'HTTPNotAcceptable',\n    'HTTPProxyAuthenticationRequired',\n    'HTTPRequestTimeout',\n    'HTTPConflict',\n    'HTTPGone',\n    'HTTPLengthRequired',\n    'HTTPPreconditionFailed',\n    'HTTPRequestEntityTooLarge',\n    'HTTPRequestURITooLong',\n    'HTTPUnsupportedMediaType',\n    'HTTPRequestRangeNotSatisfiable',\n    'HTTPExpectationFailed',\n    'HTTPServerError',\n    'HTTPInternalServerError',\n    'HTTPNotImplemented',\n    'HTTPBadGateway',\n    'HTTPServiceUnavailable',\n    'HTTPGatewayTimeout',\n    'HTTPVersionNotSupported',\n]\n\n\nsentinel = object()\nretype = type(re.compile('@'))\n\n\nclass HeadersMixin:\n\n    _content_type = None\n    _content_dict = None\n    _stored_content_type = sentinel\n\n    def _parse_content_type(self, raw):\n        self._stored_content_type = raw\n        if raw is None:\n            # default value according to RFC 2616\n            self._content_type = 'application/octet-stream'\n            self._content_dict = {}\n        else:\n            self._content_type, self._content_dict = cgi.parse_header(raw)\n\n    @property\n    def content_type(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        \"\"\"The value of content part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get(_CONTENT_TYPE)\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_type\n\n    @property\n    def charset(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        \"\"\"The value of charset part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get(_CONTENT_TYPE)\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_dict.get('charset')\n\n    @property\n    def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):\n        \"\"\"The value of Content-Length HTTP header.\"\"\"\n        l = self.headers.get(_CONTENT_LENGTH)\n        if l is None:\n            return None\n        else:\n            return int(l)\n\n\nFileField = collections.namedtuple('Field', 'name filename file content_type')\n\n\n############################################################\n# HTTP Request\n############################################################\n\n\nclass Request(dict, HeadersMixin):\n\n    def __init__(self, app, message, payload, transport, reader, writer, *,\n                 _HOST=hdrs.HOST):\n        self._app = app\n        self._version = message.version\n        self._transport = transport\n        self._reader = reader\n        self._writer = writer\n        self._method = message.method\n        self._host = message.headers.get(_HOST)\n        self._path_qs = message.path\n        res = urlsplit(message.path)\n        self._path = unquote(res.path)\n        self._query_string = res.query\n        self._post = None\n        self._post_files_cache = None\n        self._headers = CIMultiDictProxy(message.headers)\n\n        if self._version < HttpVersion11:\n            self._keep_alive = False\n        elif message.should_close:\n            self._keep_alive = False\n        else:\n            self._keep_alive = True\n\n        # matchdict, route_name, handler\n        # or information about traversal lookup\n        self._match_info = None  # initialized after route resolving\n\n        self._payload = payload\n        self._cookies = None\n\n        self._read_bytes = None\n\n    @property\n    def method(self):\n        \"\"\"Read only property for getting HTTP method.\n\n        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.\n        \"\"\"\n        return self._method\n\n    @property\n    def version(self):\n        \"\"\"Read only property for getting HTTP version of request.\n\n        Returns aiohttp.protocol.HttpVersion instance.\n        \"\"\"\n        return self._version\n\n    @property\n    def host(self):\n        \"\"\"Read only property for getting *HOST* header of request.\n\n        Returns str or None if HTTP request has no HOST header.\n        \"\"\"\n        return self._host\n\n    @property\n    def path_qs(self):\n        \"\"\"The URL including PATH_INFO and the query string.\n\n        E.g, /app/blog?id=10\n        \"\"\"\n        return self._path_qs\n\n    @property\n    def path(self):\n        \"\"\"The URL including *PATH INFO* without the host or scheme.\n\n        E.g., ``/app/blog``\n        \"\"\"\n        return self._path\n\n    @property\n    def query_string(self):\n        \"\"\"The query string in the URL.\n\n        E.g., id=10\n        \"\"\"\n        return self._query_string\n\n    @reify\n    def GET(self):\n        \"\"\"A multidict with all the variables in the query string.\n\n        Lazy property.\n        \"\"\"\n        return MultiDictProxy(MultiDict(parse_qsl(self._query_string)))\n\n    @reify\n    def POST(self):\n        \"\"\"A multidict with all the variables in the POST parameters.\n\n        post() methods has to be called before using this attribute.\n        \"\"\"\n        if self._post is None:\n            raise RuntimeError(\"POST is not available before post()\")\n        return self._post\n\n    @property\n    def headers(self):\n        \"\"\"A case-insensitive multidict with all headers.\n\n        Lazy property.\n        \"\"\"\n        return self._headers\n\n    @property\n    def keep_alive(self):\n        \"\"\"Is keepalive enabled by client?\"\"\"\n        return self._keep_alive\n\n    @property\n    def match_info(self):\n        \"\"\"Result of route resolving.\"\"\"\n        return self._match_info\n\n    @property\n    def app(self):\n        \"\"\"Application instance.\"\"\"\n        return self._app\n\n    @property\n    def transport(self):\n        \"\"\"Transport used for request processing.\"\"\"\n        return self._transport\n\n    @property\n    def cookies(self):\n        \"\"\"Return request cookies.\n\n        A read-only dictionary-like object.\n        \"\"\"\n        if self._cookies is None:\n            raw = self.headers.get(hdrs.COOKIE, '')\n            parsed = http.cookies.SimpleCookie(raw)\n            self._cookies = MappingProxyType(\n                {key: val.value for key, val in parsed.items()})\n        return self._cookies\n\n    @property\n    def payload(self):\n        \"\"\"Return raw payload stream.\"\"\"\n        warnings.warn('use Request.content instead', DeprecationWarning)\n        return self._payload\n\n    @property\n    def content(self):\n        \"\"\"Return raw payload stream.\"\"\"\n        return self._payload\n\n    @asyncio.coroutine\n    def release(self):\n        \"\"\"Release request.\n\n        Eat unread part of HTTP BODY if present.\n        \"\"\"\n        chunk = yield from self._payload.readany()\n        while chunk is not EOF_MARKER or chunk:\n            chunk = yield from self._payload.readany()\n\n    @asyncio.coroutine\n    def read(self):\n        \"\"\"Read request body if present.\n\n        Returns bytes object with full request content.\n        \"\"\"\n        if self._read_bytes is None:\n            body = bytearray()\n            while True:\n                chunk = yield from self._payload.readany()\n                body.extend(chunk)\n                if chunk is EOF_MARKER:\n                    break\n            self._read_bytes = bytes(body)\n        return self._read_bytes\n\n    @asyncio.coroutine\n    def text(self):\n        \"\"\"Return BODY as text using encoding from .charset.\"\"\"\n        bytes_body = yield from self.read()\n        encoding = self.charset or 'utf-8'\n        return bytes_body.decode(encoding)\n\n    @asyncio.coroutine\n    def json(self, *, loader=json.loads):\n        \"\"\"Return BODY as JSON.\"\"\"\n        body = yield from self.text()\n        return loader(body)\n\n    @asyncio.coroutine\n    def post(self):\n        \"\"\"Return POST parameters.\"\"\"\n        if self._post is not None:\n            return self._post\n        if self.method not in ('POST', 'PUT', 'PATCH'):\n            self._post = MultiDictProxy(MultiDict())\n            return self._post\n\n        content_type = self.content_type\n        if (content_type not in ('',\n                                 'application/x-www-form-urlencoded',\n                                 'multipart/form-data')):\n            self._post = MultiDictProxy(MultiDict())\n            return self._post\n\n        body = yield from self.read()\n        content_charset = self.charset or 'utf-8'\n\n        environ = {'REQUEST_METHOD': self.method,\n                   'CONTENT_LENGTH': str(len(body)),\n                   'QUERY_STRING': '',\n                   'CONTENT_TYPE': self.headers.get(hdrs.CONTENT_TYPE)}\n\n        fs = cgi.FieldStorage(fp=io.BytesIO(body),\n                              environ=environ,\n                              keep_blank_values=True,\n                              encoding=content_charset)\n\n        supported_transfer_encoding = {\n            'base64': binascii.a2b_base64,\n            'quoted-printable': binascii.a2b_qp\n        }\n\n        out = MultiDict()\n        for field in fs.list or ():\n            transfer_encoding = field.headers.get(\n                'Content-Transfer-Encoding', None)\n            if field.filename:\n                ff = FileField(field.name,\n                               field.filename,\n                               field.file,  # N.B. file closed error\n                               field.type)\n                if self._post_files_cache is None:\n                    self._post_files_cache = {}\n                self._post_files_cache[field.name] = field\n                out.add(field.name, ff)\n            else:\n                value = field.value\n                if transfer_encoding in supported_transfer_encoding:\n                    # binascii accepts bytes\n                    value = value.encode('utf-8')\n                    value = supported_transfer_encoding[\n                        transfer_encoding](value)\n                out.add(field.name, value)\n\n        self._post = MultiDictProxy(out)\n        return self._post\n\n    def __repr__(self):\n        return \"<{} {} {} >\".format(self.__class__.__name__,\n                                    self.method, self.path)\n\n\n############################################################\n# HTTP Response classes\n############################################################\n\n\nclass StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._chunked = False\n        self._chunk_size = None\n        self._compression = False\n        self._compression_force = False\n        self._headers = CIMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add(hdrs.SET_COOKIE, value)\n\n    @property\n    def started(self):\n        return self._resp_impl is not None\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def chunked(self):\n        return self._chunked\n\n    @property\n    def compression(self):\n        return self._compression\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    def enable_chunked_encoding(self, chunk_size=None):\n        \"\"\"Enables automatic chunked transfer encoding.\"\"\"\n        self._chunked = True\n        self._chunk_size = chunk_size\n\n    def enable_compression(self, force=False):\n        \"\"\"Enables response compression with `deflate` encoding.\"\"\"\n        self._compression = True\n        self._compression_force = force\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path='/',\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path='/'):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers[hdrs.CONTENT_LENGTH] = str(value)\n        elif hdrs.CONTENT_LENGTH in self.headers:\n            del self.headers[hdrs.CONTENT_LENGTH]\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value).lower()\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers[CONTENT_TYPE] = ctype\n\n    def _start_pre_check(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            else:\n                return self._resp_impl\n        else:\n            return None\n\n    def start(self, request):\n        resp_impl = self._start_pre_check(request)\n        if resp_impl is not None:\n            return resp_impl\n\n        self._req = request\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        if self._compression:\n            if (self._compression_force or\n                    'deflate' in request.headers.get(\n                        hdrs.ACCEPT_ENCODING, '')):\n                resp_impl.add_compression_filter()\n\n        if self._chunked:\n            resp_impl.enable_chunked_encoding()\n            if self._chunk_size:\n                resp_impl.add_chunking_filter(self._chunk_size)\n\n        headers = self.headers.items()\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)' %\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def drain(self):\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n        yield from self._resp_impl.transport.drain()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True\n\n    def __repr__(self):\n        if self.started:\n            info = \"{} {} \".format(self._req.method, self._req.path)\n        else:\n            info = \"not started\"\n        return \"<{} {} {}>\".format(self.__class__.__name__,\n                                   self.reason, info)\n\n\nclass Response(StreamResponse):\n\n    def __init__(self, *, body=None, status=200,\n                 reason=None, headers=None,\n                 text=None, content_type=None):\n        super().__init__(status=status, reason=reason)\n\n        if headers is not None:\n            self.headers.extend(headers)\n\n        if body is not None and text is not None:\n            raise ValueError(\"body and text are not allowed together.\")\n\n        if text is not None:\n            if hdrs.CONTENT_TYPE not in self.headers:\n                # fast path for filling headers\n                if not isinstance(text, str):\n                    raise TypeError('text argument must be str (%r)' %\n                                    type(text))\n                if content_type is None:\n                    content_type = 'text/plain'\n                self.headers[hdrs.CONTENT_TYPE] = (\n                    content_type + '; charset=utf-8')\n                self._content_type = content_type\n                self._content_dict = {'charset': 'utf-8'}\n                self.body = text.encode('utf-8')\n            else:\n                self.text = text\n        else:\n            if content_type:\n                self.content_type = content_type\n            if body is not None:\n                self.body = body\n            else:\n                self.body = None\n\n    @property\n    def body(self):\n        return self._body\n\n    @body.setter\n    def body(self, body):\n        if body is not None and not isinstance(body, bytes):\n            raise TypeError('body argument must be bytes (%r)' % type(body))\n        self._body = body\n        if body is not None:\n            self.content_length = len(body)\n        else:\n            self.content_length = 0\n\n    @property\n    def text(self):\n        return self._body.decode(self.charset or 'utf-8')\n\n    @text.setter\n    def text(self, text):\n        if text is not None and not isinstance(text, str):\n            raise TypeError('text argument must be str (%r)' % type(text))\n\n        if self.content_type == 'application/octet-stream':\n            self.content_type = 'text/plain'\n        if self.charset is None:\n            self.charset = 'utf-8'\n\n        self.body = text.encode(self.charset)\n\n    @asyncio.coroutine\n    def write_eof(self):\n        body = self._body\n        if body is not None:\n            self.write(body)\n        yield from super().write_eof()\n\n\nclass WebSocketResponse(StreamResponse):\n\n    def __init__(self, *, protocols=()):\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._protocol = None\n        self._writer = None\n        self._reader = None\n        self._closing = False\n        self._loop = None\n        self._closing_fut = None\n\n    def start(self, request):\n        # make pre-check to don't hide it by do_handshake() exceptions\n        resp_impl = self._start_pre_check(request)\n        if resp_impl is not None:\n            return resp_impl\n\n        try:\n            status, headers, parser, writer, protocol = do_handshake(\n                request.method, request.headers, request.transport,\n                self._protocols)\n        except HttpProcessingError as err:\n            if err.code == 405:\n                raise HTTPMethodNotAllowed(request.method, ['GET'])\n            elif err.code == 400:\n                raise HTTPBadRequest(text=err.message, headers=err.headers)\n            else:  # pragma: no cover\n                raise HTTPInternalServerError() from err\n\n        if self.status != status:\n            self.set_status(status)\n        for k, v in headers:\n            self.headers[k] = v\n        self.force_close()\n\n        resp_impl = super().start(request)\n\n        self._reader = request._reader.set_parser(parser)\n        self._writer = writer\n        self._protocol = protocol\n        self._loop = request.app.loop\n        self._closing_fut = asyncio.Future(loop=self._loop)\n\n        return resp_impl\n\n    def can_start(self, request):\n        if self._writer is not None:\n            raise RuntimeError('Already started')\n        try:\n            _, _, _, _, protocol = do_handshake(\n                request.method, request.headers, request.transport,\n                self._protocols)\n        except HttpProcessingError:\n            return False, None\n        else:\n            return True, protocol\n\n    @property\n    def closing(self):\n        return self._closing\n\n    @property\n    def protocol(self):\n        return self._protocol\n\n    def ping(self, message='b'):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        self._writer.ping(message)\n\n    def pong(self, message='b'):\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        self._writer.pong(message)\n\n    def send_str(self, data):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        if not isinstance(data, str):\n            raise TypeError('data argument must be str (%r)' % type(data))\n        self._writer.send(data, binary=False)\n\n    def send_bytes(self, data):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if self._closing:\n            raise RuntimeError('websocket connection is closing')\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)' %\n                            type(data))\n        self._writer.send(data, binary=True)\n\n    def close(self, *, code=1000, message=b''):\n        if self._writer is None:\n            raise RuntimeError('Call .start() first')\n        if not self._closing:\n            self._closing = True\n            self._writer.close(code, message)\n        else:\n            raise RuntimeError('Already closing')\n\n    @asyncio.coroutine\n    def wait_closed(self):\n        if self._closing_fut is None:\n            raise RuntimeError('Call .start() first')\n        yield from self._closing_fut\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self.wait_closed()\n        self._eof_sent = True\n\n    @asyncio.coroutine\n    def receive_msg(self):\n        if self._reader is None:\n            raise RuntimeError('Call .start() first')\n        while True:\n            try:\n                msg = yield from self._reader.read()\n            except Exception as exc:\n                self._closing_fut.set_exception(exc)\n                raise\n\n            if msg.tp == MSG_CLOSE:\n                if self._closing:\n                    exc = WSClientDisconnectedError(msg.data, msg.extra)\n                    self._closing_fut.set_exception(exc)\n                    raise exc\n                else:\n                    self._closing = True\n                    self._writer.close(msg.data, msg.extra)\n                    yield from self.drain()\n                    exc = WSClientDisconnectedError(msg.data, msg.extra)\n                    self._closing_fut.set_exception(exc)\n                    raise exc\n            elif not self._closing:\n                if msg.tp == MSG_PING:\n                    self._writer.pong(msg.data)\n                elif msg.tp in (MSG_TEXT, MSG_BINARY):\n                    return msg\n\n    @asyncio.coroutine\n    def receive_str(self):\n        msg = yield from self.receive_msg()\n        if msg.tp != MSG_TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not str\".format(msg.tp, msg.data))\n        return msg.data\n\n    @asyncio.coroutine\n    def receive_bytes(self):\n        msg = yield from self.receive_msg()\n        if msg.tp != MSG_BINARY:\n            raise TypeError(\n                \"Received message {}:{!r} is not bytes\".format(msg.tp,\n                                                               msg.data))\n        return msg.data\n\n    def write(self, data):\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n\n############################################################\n# HTTP Exceptions\n############################################################\n\nclass HTTPException(Response, Exception):\n\n    # You should set in subclasses:\n    # status = 200\n\n    status_code = None\n\n    def __init__(self, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        Response.__init__(self, status=self.status_code,\n                          headers=headers, reason=reason,\n                          body=body, text=text, content_type=content_type)\n        Exception.__init__(self, self.reason)\n        if self.body is None:\n            self.text = \"{}: {}\".format(self.status, self.reason)\n\n\nclass HTTPError(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 400s and 500s.\"\"\"\n\n\nclass HTTPRedirection(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 300s.\"\"\"\n\n\nclass HTTPSuccessful(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 200s.\"\"\"\n\n\nclass HTTPOk(HTTPSuccessful):\n    status_code = 200\n\n\nclass HTTPCreated(HTTPSuccessful):\n    status_code = 201\n\n\nclass HTTPAccepted(HTTPSuccessful):\n    status_code = 202\n\n\nclass HTTPNonAuthoritativeInformation(HTTPSuccessful):\n    status_code = 203\n\n\nclass HTTPNoContent(HTTPSuccessful):\n    status_code = 204\n\n\nclass HTTPResetContent(HTTPSuccessful):\n    status_code = 205\n\n\nclass HTTPPartialContent(HTTPSuccessful):\n    status_code = 206\n\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass _HTTPMove(HTTPRedirection):\n\n    def __init__(self, location, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Location'] = location\n        self.location = location\n\n\nclass HTTPMultipleChoices(_HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(_HTTPMove):\n    status_code = 301\n\n\nclass HTTPFound(_HTTPMove):\n    status_code = 302\n\n\n# This one is safe after a POST (the redirected location will be\n# retrieved with GET):\nclass HTTPSeeOther(_HTTPMove):\n    status_code = 303\n\n\nclass HTTPNotModified(HTTPRedirection):\n    # FIXME: this should include a date or etag header\n    status_code = 304\n\n\nclass HTTPUseProxy(_HTTPMove):\n    # Not a move, but looks a little like one\n    status_code = 305\n\n\nclass HTTPTemporaryRedirect(_HTTPMove):\n    status_code = 307\n\n\n############################################################\n# 4xx client error\n############################################################\n\n\nclass HTTPClientError(HTTPError):\n    pass\n\n\nclass HTTPBadRequest(HTTPClientError):\n    status_code = 400\n\n\nclass HTTPUnauthorized(HTTPClientError):\n    status_code = 401\n\n\nclass HTTPPaymentRequired(HTTPClientError):\n    status_code = 402\n\n\nclass HTTPForbidden(HTTPClientError):\n    status_code = 403\n\n\nclass HTTPNotFound(HTTPClientError):\n    status_code = 404\n\n\nclass HTTPMethodNotAllowed(HTTPClientError):\n    status_code = 405\n\n    def __init__(self, method, allowed_methods, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        allow = ','.join(sorted(allowed_methods))\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Allow'] = allow\n        self.allowed_methods = allowed_methods\n        self.method = method.upper()\n\n\nclass HTTPNotAcceptable(HTTPClientError):\n    status_code = 406\n\n\nclass HTTPProxyAuthenticationRequired(HTTPClientError):\n    status_code = 407\n\n\nclass HTTPRequestTimeout(HTTPClientError):\n    status_code = 408\n\n\nclass HTTPConflict(HTTPClientError):\n    status_code = 409\n\n\nclass HTTPGone(HTTPClientError):\n    status_code = 410\n\n\nclass HTTPLengthRequired(HTTPClientError):\n    status_code = 411\n\n\nclass HTTPPreconditionFailed(HTTPClientError):\n    status_code = 412\n\n\nclass HTTPRequestEntityTooLarge(HTTPClientError):\n    status_code = 413\n\n\nclass HTTPRequestURITooLong(HTTPClientError):\n    status_code = 414\n\n\nclass HTTPUnsupportedMediaType(HTTPClientError):\n    status_code = 415\n\n\nclass HTTPRequestRangeNotSatisfiable(HTTPClientError):\n    status_code = 416\n\n\nclass HTTPExpectationFailed(HTTPClientError):\n    status_code = 417\n\n\n############################################################\n# 5xx Server Error\n############################################################\n#  Response status codes beginning with the digit \"5\" indicate cases in\n#  which the server is aware that it has erred or is incapable of\n#  performing the request. Except when responding to a HEAD request, the\n#  server SHOULD include an entity containing an explanation of the error\n#  situation, and whether it is a temporary or permanent condition. User\n#  agents SHOULD display any included entity to the user. These response\n#  codes are applicable to any request method.\n\n\nclass HTTPServerError(HTTPError):\n    pass\n\n\nclass HTTPInternalServerError(HTTPServerError):\n    status_code = 500\n\n\nclass HTTPNotImplemented(HTTPServerError):\n    status_code = 501\n\n\nclass HTTPBadGateway(HTTPServerError):\n    status_code = 502\n\n\nclass HTTPServiceUnavailable(HTTPServerError):\n    status_code = 503\n\n\nclass HTTPGatewayTimeout(HTTPServerError):\n    status_code = 504\n\n\nclass HTTPVersionNotSupported(HTTPServerError):\n    status_code = 505\n\n\n############################################################\n# UrlDispatcher implementation\n############################################################\n\n\nclass UrlMappingMatchInfo(dict, AbstractMatchInfo):\n\n    def __init__(self, match_dict, route):\n        super().__init__(match_dict)\n        self._route = route\n\n    @property\n    def handler(self):\n        return self._route.handler\n\n    @property\n    def route(self):\n        return self._route\n\n    def __repr__(self):\n        return \"<MatchInfo {}: {}>\".format(super().__repr__(), self._route)\n\n\n@asyncio.coroutine\ndef defaultExpectHandler(request):\n    \"\"\"Default handler for Except: 100-continue\"\"\"\n    if request.version == HttpVersion11:\n        request.transport.write(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\")\n\n\nclass Route(metaclass=abc.ABCMeta):\n\n    def __init__(self, method, handler, name, expect_handler=None):\n        if expect_handler is None:\n            expect_handler = defaultExpectHandler\n        assert asyncio.iscoroutinefunction(expect_handler), \\\n            'Coroutine is expected, got {!r}'.format(expect_handler)\n\n        self._method = method\n        self._handler = handler\n        self._name = name\n        self._expect_handler = expect_handler\n\n    @property\n    def method(self):\n        return self._method\n\n    @property\n    def handler(self):\n        return self._handler\n\n    @property\n    def name(self):\n        return self._name\n\n    @abc.abstractmethod\n    def match(self, path):\n        \"\"\"Return dict with info for given path or\n        None if route cannot process path.\"\"\"\n\n    @abc.abstractmethod\n    def url(self, **kwargs):\n        \"\"\"Construct url for route with additional params.\"\"\"\n\n    @asyncio.coroutine\n    def handle_expect_header(self, request):\n        return (yield from self._expect_handler(request))\n\n    @staticmethod\n    def _append_query(url, query):\n        if query is not None:\n            return url + \"?\" + urlencode(query)\n        else:\n            return url\n\n\nclass PlainRoute(Route):\n\n    def __init__(self, method, handler, name, path, expect_handler=None):\n        super().__init__(method, handler, name, expect_handler=expect_handler)\n        self._path = path\n\n    def match(self, path):\n        # string comparison is about 10 times faster than regexp matching\n        if self._path == path:\n            return {}\n        else:\n            return None\n\n    def url(self, *, query=None):\n        return self._append_query(self._path, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<PlainRoute {name}[{method}] {path} -> {handler!r}\".format(\n            name=name, method=self.method, path=self._path,\n            handler=self.handler)\n\n\nclass DynamicRoute(Route):\n\n    def __init__(self, method, handler, name, pattern, formatter,\n                 expect_handler=None):\n        super().__init__(method, handler, name, expect_handler=expect_handler)\n        self._pattern = pattern\n        self._formatter = formatter\n\n    def match(self, path):\n        match = self._pattern.match(path)\n        if match is None:\n            return None\n        else:\n            return match.groupdict()\n\n    def url(self, *, parts, query=None):\n        url = self._formatter.format_map(parts)\n        return self._append_query(url, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return (\"<DynamicRoute {name}[{method}] {formatter} -> {handler!r}\"\n                .format(name=name, method=self.method,\n                        formatter=self._formatter, handler=self.handler))\n\n\nclass StaticRoute(Route):\n\n    limit = 8192\n\n    def __init__(self, name, prefix, directory, expect_handler=None):\n        assert prefix.startswith('/'), prefix\n        assert prefix.endswith('/'), prefix\n        super().__init__(\n            'GET', self.handle, name, expect_handler=expect_handler)\n        self._prefix = prefix\n        self._prefix_len = len(self._prefix)\n        self._directory = directory\n\n    def match(self, path):\n        if not path.startswith(self._prefix):\n            return None\n        return {'filename': path[self._prefix_len:]}\n\n    def url(self, *, filename, query=None):\n        while filename.startswith('/'):\n            filename = filename[1:]\n        url = self._prefix + filename\n        return self._append_query(url, query)\n\n    @asyncio.coroutine\n    def handle(self, request):\n        resp = StreamResponse()\n        filename = request.match_info['filename']\n        filepath = os.path.join(self._directory, filename)\n        if '..' in filename:\n            raise HTTPNotFound()\n        if not os.path.exists(filepath) or not os.path.isfile(filepath):\n            raise HTTPNotFound()\n\n        ct = mimetypes.guess_type(filename)[0]\n        if not ct:\n            ct = 'application/octet-stream'\n        resp.content_type = ct\n\n        file_size = os.stat(filepath).st_size\n        single_chunk = file_size < self.limit\n\n        if single_chunk:\n            resp.content_length = file_size\n        resp.start(request)\n\n        with open(filepath, 'rb') as f:\n            chunk = f.read(self.limit)\n            if single_chunk:\n                resp.write(chunk)\n            else:\n                while chunk:\n                    resp.write(chunk)\n                    chunk = f.read(self.limit)\n\n        return resp\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<StaticRoute {name}[{method}] {path} -> {directory!r}\".format(\n            name=name, method=self.method, path=self._prefix,\n            directory=self._directory)\n\n\nclass _NotFoundMatchInfo(UrlMappingMatchInfo):\n\n    def __init__(self):\n        super().__init__({}, None)\n\n    @property\n    def handler(self):\n        return self._not_found\n\n    @property\n    def route(self):\n        return None\n\n    @asyncio.coroutine\n    def _not_found(self, request):\n        raise HTTPNotFound()\n\n    def __repr__(self):\n        return \"<MatchInfo: not found>\"\n\n\nclass _MethodNotAllowedMatchInfo(UrlMappingMatchInfo):\n\n    def __init__(self, method, allowed_methods):\n        super().__init__({}, None)\n        self._method = method\n        self._allowed_methods = allowed_methods\n\n    @property\n    def handler(self):\n        return self._not_allowed\n\n    @property\n    def route(self):\n        return None\n\n    @asyncio.coroutine\n    def _not_allowed(self, request):\n        raise HTTPMethodNotAllowed(self._method, self._allowed_methods)\n\n    def __repr__(self):\n        return (\"<MatchInfo: method {} is not allowed (allowed methods: {}>\"\n                .format(self._method,\n                        ', '.join(sorted(self._allowed_methods))))\n\n\nclass UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\\}$')\n    DYN_WITH_RE = re.compile(\n        r'^\\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                return _MethodNotAllowedMatchInfo(method, allowed_methods)\n            else:\n                return _NotFoundMatchInfo()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler,\n                  *, name=None, expect_handler=None):\n\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n\n        if isinstance(path, retype):\n            route = DynamicRoute(\n                method, handler, name, path,\n                path.pattern, expect_handler=expect_handler)\n\n            self._register_endpoint(route)\n            return route\n\n        assert path.startswith('/')\n        parts = []\n        factory = PlainRoute\n        format_parts = []\n        for part in path.split('/'):\n            if not part:\n                continue\n            match = self.DYN.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             self.GOOD + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n\n            match = self.DYN_WITH_RE.match(part)\n            if match:\n                parts.append('(?P<' + match.group('var') + '>' +\n                             match.group('re') + ')')\n                factory = DynamicRoute\n                format_parts.append('{'+match.group('var')+'}')\n                continue\n            if self.PLAIN.match(part):\n                parts.append(re.escape(part))\n                format_parts.append(part)\n                continue\n            raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(\n                method, handler, name, path, expect_handler=expect_handler)\n        else:\n            pattern = '/' + '/'.join(parts)\n            formatter = '/' + '/'.join(format_parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n                formatter += '/'\n            try:\n                compiled = re.compile('^' + pattern + '$')\n            except re.error as exc:\n                raise ValueError(\n                    \"Bad pattern '{}': {}\".format(pattern, exc)) from None\n            route = DynamicRoute(\n                method, handler, name, compiled,\n                formatter, expect_handler=expect_handler)\n\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None, expect_handler=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)\n        self._register_endpoint(route)\n        return route\n\n\n############################################################\n# Application implementation\n############################################################\n\n\nclass RequestHandler(ServerHttpProtocol):\n\n    def __init__(self, manager, app, router, **kwargs):\n        super().__init__(**kwargs)\n\n        self._manager = manager\n        self._app = app\n        self._router = router\n        self._middlewares = app.middlewares\n\n    def connection_made(self, transport):\n        super().connection_made(transport)\n\n        self._manager.connection_made(self, transport)\n\n    def connection_lost(self, exc):\n        self._manager.connection_lost(self, exc)\n\n        super().connection_lost(exc)\n\n    @asyncio.coroutine\n    def handle_request(self, message, payload):\n        now = self._loop.time()\n\n        app = self._app\n        request = Request(app, message, payload,\n                          self.transport, self.reader, self.writer)\n        try:\n            match_info = yield from self._router.resolve(request)\n\n            assert isinstance(match_info, AbstractMatchInfo), match_info\n\n            resp = None\n            request._match_info = match_info\n            if request.headers.get(hdrs.EXPECT, '').lower() == \"100-continue\":\n                resp = (\n                    yield from match_info.route.handle_expect_header(request))\n\n            if resp is None:\n                handler = match_info.handler\n                for factory in reversed(self._middlewares):\n                    handler = yield from factory(app, handler)\n                resp = yield from handler(request)\n\n            if not isinstance(resp, StreamResponse):\n                raise RuntimeError(\n                    (\"Handler {!r} should return response instance, \"\n                     \"got {!r} [middlewares {!r}]\").format(\n                         match_info.handler,\n                         type(resp),\n                         self._middlewares))\n        except HTTPException as exc:\n            resp = exc\n\n        resp_msg = resp.start(request)\n        yield from resp.write_eof()\n\n        # notify server about keep-alive\n        self.keep_alive(resp_msg.keep_alive())\n\n        # log access\n        self.log_access(message, None, resp_msg, self._loop.time() - now)\n\n\nclass RequestHandlerFactory:\n\n    def __init__(self, app, router, *,\n                 handler=RequestHandler, loop=None, **kwargs):\n        self._app = app\n        self._router = router\n        self._handler = handler\n        self._loop = loop\n        self._connections = {}\n        self._kwargs = kwargs\n        self._kwargs.setdefault('logger', app.logger)\n\n    @property\n    def connections(self):\n        return list(self._connections.keys())\n\n    def connection_made(self, handler, transport):\n        self._connections[handler] = transport\n\n    def connection_lost(self, handler, exc=None):\n        if handler in self._connections:\n            del self._connections[handler]\n\n    @asyncio.coroutine\n    def finish_connections(self, timeout=None):\n        for handler in self._connections.keys():\n            handler.closing()\n\n        @asyncio.coroutine\n        def cleanup():\n            sleep = 0.05\n            while self._connections:\n                yield from asyncio.sleep(sleep, loop=self._loop)\n                if sleep < 5:\n                    sleep = sleep * 2\n\n        if timeout:\n            try:\n                yield from asyncio.wait_for(\n                    cleanup(), timeout, loop=self._loop)\n            except asyncio.TimeoutError:\n                self._app.logger.warning(\n                    \"Not all connections are closed (pending: %d)\",\n                    len(self._connections))\n\n        for transport in self._connections.values():\n            transport.close()\n\n        self._connections.clear()\n\n    def __call__(self):\n        return self._handler(\n            self, self._app, self._router, loop=self._loop, **self._kwargs)\n\n\nclass Application(dict):\n\n    def __init__(self, *, logger=web_logger, loop=None,\n                 router=None, handler_factory=RequestHandlerFactory,\n                 middlewares=()):\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if router is None:\n            router = UrlDispatcher()\n        assert isinstance(router, AbstractRouter), router\n\n        self._router = router\n        self._handler_factory = handler_factory\n        self._finish_callbacks = []\n        self._loop = loop\n        self.logger = logger\n\n        for factory in middlewares:\n            assert asyncio.iscoroutinefunction(factory), factory\n        self._middlewares = tuple(middlewares)\n\n    @property\n    def router(self):\n        return self._router\n\n    @property\n    def loop(self):\n        return self._loop\n\n    @property\n    def middlewares(self):\n        return self._middlewares\n\n    def make_handler(self, **kwargs):\n        return self._handler_factory(\n            self, self.router, loop=self.loop, **kwargs)\n\n    @asyncio.coroutine\n    def finish(self):\n        callbacks = self._finish_callbacks\n        self._finish_callbacks = []\n\n        for (cb, args, kwargs) in callbacks:\n            try:\n                res = cb(self, *args, **kwargs)\n                if (asyncio.iscoroutine(res) or\n                        isinstance(res, asyncio.Future)):\n                    yield from res\n            except Exception as exc:\n                self._loop.call_exception_handler({\n                    'message': \"Error in finish callback\",\n                    'exception': exc,\n                    'application': self,\n                })\n\n    def register_on_finish(self, func, *args, **kwargs):\n        self._finish_callbacks.insert(0, (func, args, kwargs))\n\n    def __call__(self):\n        \"\"\"gunicorn compatibility\"\"\"\n        return self\n\n    def __repr__(self):\n        return \"<Application>\"\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:** Adding Route with Regex and Optional Trailing Slash Raises ValueError

**Description:**
When attempting to add a route that includes a regular expression with an optional trailing slash, the URL dispatcher raises a `ValueError`. This prevents the route from being registered correctly, disrupting the application's routing functionality.

**Example Code:**
```python
handler = make_handler()
router.add_route('GET', '/handler/{to:[^/]+/?}', handler)
```

**Error Message:**
```
ValueError: Invalid path '/handler/{to:[^/]+/?}'['{to:[^']
```

**Expected Behavior:**
The route should be added successfully, allowing URLs that match the pattern with or without a trailing slash. For example, both `/handler/1234` and `/handler/1234/` should be valid and correctly routed to the specified handler.

**Actual Behavior:**
Adding the route raises a `ValueError`, indicating that the provided path is invalid. This error prevents the route from being registered, causing any matching URLs to fail to resolve correctly.

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

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

--- BEGIN FILE ---
```
### aiohttp/web.py
import abc
import asyncio
import binascii
import collections
import mimetypes
import cgi
import http.cookies
import io
import json
import re
import os
import warnings

from urllib.parse import urlsplit, parse_qsl, urlencode, unquote
from types import MappingProxyType

from . import hdrs
from .abc import AbstractRouter, AbstractMatchInfo
from .helpers import reify
from .log import web_logger
from .multidict import (CIMultiDictProxy,
                        CIMultiDict,
                        MultiDictProxy,
                        MultiDict)
from .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11
from .server import ServerHttpProtocol
from .streams import EOF_MARKER
from .websocket import do_handshake, MSG_BINARY, MSG_CLOSE, MSG_PING, MSG_TEXT
from .errors import HttpProcessingError, WSClientDisconnectedError


__all__ = [
    'WSClientDisconnectedError',
    'Application',
    'HttpVersion',
    'RequestHandler',
    'RequestHandlerFactory',
    'Request',
    'StreamResponse',
    'Response',
    'WebSocketResponse',
    'UrlDispatcher',
    'UrlMappingMatchInfo',
    'HTTPException',
    'HTTPError',
    'HTTPRedirection',
    'HTTPSuccessful',
    'HTTPOk',
    'HTTPCreated',
    'HTTPAccepted',
    'HTTPNonAuthoritativeInformation',
    'HTTPNoContent',
    'HTTPResetContent',
    'HTTPPartialContent',
    'HTTPMultipleChoices',
    'HTTPMovedPermanently',
    'HTTPFound',
    'HTTPSeeOther',
    'HTTPNotModified',
    'HTTPUseProxy',
    'HTTPTemporaryRedirect',
    'HTTPClientError',
    'HTTPBadRequest',
    'HTTPUnauthorized',
    'HTTPPaymentRequired',
    'HTTPForbidden',
    'HTTPNotFound',
    'HTTPMethodNotAllowed',
    'HTTPNotAcceptable',
    'HTTPProxyAuthenticationRequired',
    'HTTPRequestTimeout',
    'HTTPConflict',
    'HTTPGone',
    'HTTPLengthRequired',
    'HTTPPreconditionFailed',
    'HTTPRequestEntityTooLarge',
    'HTTPRequestURITooLong',
    'HTTPUnsupportedMediaType',
    'HTTPRequestRangeNotSatisfiable',
    'HTTPExpectationFailed',
    'HTTPServerError',
    'HTTPInternalServerError',
    'HTTPNotImplemented',
    'HTTPBadGateway',
    'HTTPServiceUnavailable',
    'HTTPGatewayTimeout',
    'HTTPVersionNotSupported',
]


sentinel = object()
retype = type(re.compile('@'))


class HeadersMixin:

    _content_type = None
    _content_dict = None
    _stored_content_type = sentinel

    def _parse_content_type(self, raw):
        self._stored_content_type = raw
        if raw is None:
            # default value according to RFC 2616
            self._content_type = 'application/octet-stream'
            self._content_dict = {}
        else:
            self._content_type, self._content_dict = cgi.parse_header(raw)

    @property
    def content_type(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):
        """The value of content part for Content-Type HTTP header."""
        raw = self.headers.get(_CONTENT_TYPE)
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_type

    @property
    def charset(self, _CONTENT_TYPE=hdrs.CONTENT_TYPE):
        """The value of charset part for Content-Type HTTP header."""
        raw = self.headers.get(_CONTENT_TYPE)
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_dict.get('charset')

    @property
    def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):
        """The value of Content-Length HTTP header."""
        l = self.headers.get(_CONTENT_LENGTH)
        if l is None:
            return None
        else:
            return int(l)


FileField = collections.namedtuple('Field', 'name filename file content_type')


############################################################
# HTTP Request
############################################################


class Request(dict, HeadersMixin):

    def __init__(self, app, message, payload, transport, reader, writer, *,
                 _HOST=hdrs.HOST):
        self._app = app
        self._version = message.version
        self._transport = transport
        self._reader = reader
        self._writer = writer
        self._method = message.method
        self._host = message.headers.get(_HOST)
        self._path_qs = message.path
        res = urlsplit(message.path)
        self._path = unquote(res.path)
        self._query_string = res.query
        self._post = None
        self._post_files_cache = None
        self._headers = CIMultiDictProxy(message.headers)

        if self._version < HttpVersion11:
            self._keep_alive = False
        elif message.should_close:
            self._keep_alive = False
        else:
            self._keep_alive = True

        # matchdict, route_name, handler
        # or information about traversal lookup
        self._match_info = None  # initialized after route resolving

        self._payload = payload
        self._cookies = None

        self._read_bytes = None

    @property
    def method(self):
        """Read only property for getting HTTP method.

        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.
        """
        return self._method

    @property
    def version(self):
        """Read only property for getting HTTP version of request.

        Returns aiohttp.protocol.HttpVersion instance.
        """
        return self._version

    @property
    def host(self):
        """Read only property for getting *HOST* header of request.

        Returns str or None if HTTP request has no HOST header.
        """
        return self._host

    @property
    def path_qs(self):
        """The URL including PATH_INFO and the query string.

        E.g, /app/blog?id=10
        """
        return self._path_qs

    @property
    def path(self):
        """The URL including *PATH INFO* without the host or scheme.

        E.g., ``/app/blog``
        """
        return self._path

    @property
    def query_string(self):
        """The query string in the URL.

        E.g., id=10
        """
        return self._query_string

    @reify
    def GET(self):
        """A multidict with all the variables in the query string.

        Lazy property.
        """
        return MultiDictProxy(MultiDict(parse_qsl(self._query_string)))

    @reify
    def POST(self):
        """A multidict with all the variables in the POST parameters.

        post() methods has to be called before using this attribute.
        """
        if self._post is None:
            raise RuntimeError("POST is not available before post()")
        return self._post

    @property
    def headers(self):
        """A case-insensitive multidict with all headers.

        Lazy property.
        """
        return self._headers

    @property
    def keep_alive(self):
        """Is keepalive enabled by client?"""
        return self._keep_alive

    @property
    def match_info(self):
        """Result of route resolving."""
        return self._match_info

    @property
    def app(self):
        """Application instance."""
        return self._app

    @property
    def transport(self):
        """Transport used for request processing."""
        return self._transport

    @property
    def cookies(self):
        """Return request cookies.

        A read-only dictionary-like object.
        """
        if self._cookies is None:
            raw = self.headers.get(hdrs.COOKIE, '')
            parsed = http.cookies.SimpleCookie(raw)
            self._cookies = MappingProxyType(
                {key: val.value for key, val in parsed.items()})
        return self._cookies

    @property
    def payload(self):
        """Return raw payload stream."""
        warnings.warn('use Request.content instead', DeprecationWarning)
        return self._payload

    @property
    def content(self):
        """Return raw payload stream."""
        return self._payload

    @asyncio.coroutine
    def release(self):
        """Release request.

        Eat unread part of HTTP BODY if present.
        """
        chunk = yield from self._payload.readany()
        while chunk is not EOF_MARKER or chunk:
            chunk = yield from self._payload.readany()

    @asyncio.coroutine
    def read(self):
        """Read request body if present.

        Returns bytes object with full request content.
        """
        if self._read_bytes is None:
            body = bytearray()
            while True:
                chunk = yield from self._payload.readany()
                body.extend(chunk)
                if chunk is EOF_MARKER:
                    break
            self._read_bytes = bytes(body)
        return self._read_bytes

    @asyncio.coroutine
    def text(self):
        """Return BODY as text using encoding from .charset."""
        bytes_body = yield from self.read()
        encoding = self.charset or 'utf-8'
        return bytes_body.decode(encoding)

    @asyncio.coroutine
    def json(self, *, loader=json.loads):
        """Return BODY as JSON."""
        body = yield from self.text()
        return loader(body)

    @asyncio.coroutine
    def post(self):
        """Return POST parameters."""
        if self._post is not None:
            return self._post
        if self.method not in ('POST', 'PUT', 'PATCH'):
            self._post = MultiDictProxy(MultiDict())
            return self._post

        content_type = self.content_type
        if (content_type not in ('',
                                 'application/x-www-form-urlencoded',
                                 'multipart/form-data')):
            self._post = MultiDictProxy(MultiDict())
            return self._post

        body = yield from self.read()
        content_charset = self.charset or 'utf-8'

        environ = {'REQUEST_METHOD': self.method,
                   'CONTENT_LENGTH': str(len(body)),
                   'QUERY_STRING': '',
                   'CONTENT_TYPE': self.headers.get(hdrs.CONTENT_TYPE)}

        fs = cgi.FieldStorage(fp=io.BytesIO(body),
                              environ=environ,
                              keep_blank_values=True,
                              encoding=content_charset)

        supported_transfer_encoding = {
            'base64': binascii.a2b_base64,
            'quoted-printable': binascii.a2b_qp
        }

        out = MultiDict()
        for field in fs.list or ():
            transfer_encoding = field.headers.get(
                'Content-Transfer-Encoding', None)
            if field.filename:
                ff = FileField(field.name,
                               field.filename,
                               field.file,  # N.B. file closed error
                               field.type)
                if self._post_files_cache is None:
                    self._post_files_cache = {}
                self._post_files_cache[field.name] = field
                out.add(field.name, ff)
            else:
                value = field.value
                if transfer_encoding in supported_transfer_encoding:
                    # binascii accepts bytes
                    value = value.encode('utf-8')
                    value = supported_transfer_encoding[
                        transfer_encoding](value)
                out.add(field.name, value)

        self._post = MultiDictProxy(out)
        return self._post

    def __repr__(self):
        return "<{} {} {} >".format(self.__class__.__name__,
                                    self.method, self.path)


############################################################
# HTTP Response classes
############################################################


class StreamResponse(HeadersMixin):

    def __init__(self, *, status=200, reason=None):
        self._body = None
        self._keep_alive = None
        self._chunked = False
        self._chunk_size = None
        self._compression = False
        self._compression_force = False
        self._headers = CIMultiDict()
        self._cookies = http.cookies.SimpleCookie()
        self.set_status(status, reason)

        self._req = None
        self._resp_impl = None
        self._eof_sent = False

    def _copy_cookies(self):
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            self.headers.add(hdrs.SET_COOKIE, value)

    @property
    def started(self):
        return self._resp_impl is not None

    @property
    def status(self):
        return self._status

    @property
    def chunked(self):
        return self._chunked

    @property
    def compression(self):
        return self._compression

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None):
        self._status = int(status)
        if reason is None:
            reason = ResponseImpl.calc_reason(status)
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    def enable_chunked_encoding(self, chunk_size=None):
        """Enables automatic chunked transfer encoding."""
        self._chunked = True
        self._chunk_size = chunk_size

    def enable_compression(self, force=False):
        """Enables response compression with `deflate` encoding."""
        self._compression = True
        self._compression_force = force

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self, name, value, *, expires=None,
                   domain=None, max_age=None, path='/',
                   secure=None, httponly=None, version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]
        if expires is not None:
            c['expires'] = expires
        if domain is not None:
            c['domain'] = domain
        if max_age is not None:
            c['max-age'] = max_age
        if path is not None:
            c['path'] = path
        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path='/'):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name, '', max_age=0, domain=domain, path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            # TODO: raise error if chunked enabled
            self.headers[hdrs.CONTENT_LENGTH] = str(value)
        elif hdrs.CONTENT_LENGTH in self.headers:
            del self.headers[hdrs.CONTENT_LENGTH]

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value).lower()
        self._generate_content_type_header()

    def _generate_content_type_header(self, CONTENT_TYPE=hdrs.CONTENT_TYPE):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers[CONTENT_TYPE] = ctype

    def _start_pre_check(self, request):
        if self._resp_impl is not None:
            if self._req is not request:
                raise RuntimeError(
                    'Response has been started with different request.')
            else:
                return self._resp_impl
        else:
            return None

    def start(self, request):
        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl

        self._req = request
        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive

        resp_impl = self._resp_impl = ResponseImpl(
            request._writer,
            self._status,
            request.version,
            not keep_alive,
            self._reason)

        self._copy_cookies()

        if self._compression:
            if (self._compression_force or
                    'deflate' in request.headers.get(
                        hdrs.ACCEPT_ENCODING, '')):
                resp_impl.add_compression_filter()

        if self._chunked:
            resp_impl.enable_chunked_encoding()
            if self._chunk_size:
                resp_impl.add_chunking_filter(self._chunk_size)

        headers = self.headers.items()
        for key, val in headers:
            resp_impl.add_header(key, val)

        resp_impl.send_headers()
        return resp_impl

    def write(self, data):
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError('data argument must be byte-ish (%r)' %
                            type(data))

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._resp_impl is None:
            raise RuntimeError("Cannot call write() before start()")

        if data:
            return self._resp_impl.write(data)
        else:
            return ()

    @asyncio.coroutine
    def drain(self):
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")
        yield from self._resp_impl.transport.drain()

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self._resp_impl.write_eof()
        self._eof_sent = True

    def __repr__(self):
        if self.started:
            info = "{} {} ".format(self._req.method, self._req.path)
        else:
            info = "not started"
        return "<{} {} {}>".format(self.__class__.__name__,
                                   self.reason, info)


class Response(StreamResponse):

    def __init__(self, *, body=None, status=200,
                 reason=None, headers=None,
                 text=None, content_type=None):
        super().__init__(status=status, reason=reason)

        if headers is not None:
            self.headers.extend(headers)

        if body is not None and text is not None:
            raise ValueError("body and text are not allowed together.")

        if text is not None:
            if hdrs.CONTENT_TYPE not in self.headers:
                # fast path for filling headers
                if not isinstance(text, str):
                    raise TypeError('text argument must be str (%r)' %
                                    type(text))
                if content_type is None:
                    content_type = 'text/plain'
                self.headers[hdrs.CONTENT_TYPE] = (
                    content_type + '; charset=utf-8')
                self._content_type = content_type
                self._content_dict = {'charset': 'utf-8'}
                self.body = text.encode('utf-8')
            else:
                self.text = text
        else:
            if content_type:
                self.content_type = content_type
            if body is not None:
                self.body = body
            else:
                self.body = None

    @property
    def body(self):
        return self._body

    @body.setter
    def body(self, body):
        if body is not None and not isinstance(body, bytes):
            raise TypeError('body argument must be bytes (%r)' % type(body))
        self._body = body
        if body is not None:
            self.content_length = len(body)
        else:
            self.content_length = 0

    @property
    def text(self):
        return self._body.decode(self.charset or 'utf-8')

    @text.setter
    def text(self, text):
        if text is not None and not isinstance(text, str):
            raise TypeError('text argument must be str (%r)' % type(text))

        if self.content_type == 'application/octet-stream':
            self.content_type = 'text/plain'
        if self.charset is None:
            self.charset = 'utf-8'

        self.body = text.encode(self.charset)

    @asyncio.coroutine
    def write_eof(self):
        body = self._body
        if body is not None:
            self.write(body)
        yield from super().write_eof()


class WebSocketResponse(StreamResponse):

    def __init__(self, *, protocols=()):
        super().__init__(status=101)
        self._protocols = protocols
        self._protocol = None
        self._writer = None
        self._reader = None
        self._closing = False
        self._loop = None
        self._closing_fut = None

    def start(self, request):
        # make pre-check to don't hide it by do_handshake() exceptions
        resp_impl = self._start_pre_check(request)
        if resp_impl is not None:
            return resp_impl

        try:
            status, headers, parser, writer, protocol = do_handshake(
                request.method, request.headers, request.transport,
                self._protocols)
        except HttpProcessingError as err:
            if err.code == 405:
                raise HTTPMethodNotAllowed(request.method, ['GET'])
            elif err.code == 400:
                raise HTTPBadRequest(text=err.message, headers=err.headers)
            else:  # pragma: no cover
                raise HTTPInternalServerError() from err

        if self.status != status:
            self.set_status(status)
        for k, v in headers:
            self.headers[k] = v
        self.force_close()

        resp_impl = super().start(request)

        self._reader = request._reader.set_parser(parser)
        self._writer = writer
        self._protocol = protocol
        self._loop = request.app.loop
        self._closing_fut = asyncio.Future(loop=self._loop)

        return resp_impl

    def can_start(self, request):
        if self._writer is not None:
            raise RuntimeError('Already started')
        try:
            _, _, _, _, protocol = do_handshake(
                request.method, request.headers, request.transport,
                self._protocols)
        except HttpProcessingError:
            return False, None
        else:
            return True, protocol

    @property
    def closing(self):
        return self._closing

    @property
    def protocol(self):
        return self._protocol

    def ping(self, message='b'):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        self._writer.ping(message)

    def pong(self, message='b'):
        # unsolicited pong
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        self._writer.pong(message)

    def send_str(self, data):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        if not isinstance(data, str):
            raise TypeError('data argument must be str (%r)' % type(data))
        self._writer.send(data, binary=False)

    def send_bytes(self, data):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if self._closing:
            raise RuntimeError('websocket connection is closing')
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError('data argument must be byte-ish (%r)' %
                            type(data))
        self._writer.send(data, binary=True)

    def close(self, *, code=1000, message=b''):
        if self._writer is None:
            raise RuntimeError('Call .start() first')
        if not self._closing:
            self._closing = True
            self._writer.close(code, message)
        else:
            raise RuntimeError('Already closing')

    @asyncio.coroutine
    def wait_closed(self):
        if self._closing_fut is None:
            raise RuntimeError('Call .start() first')
        yield from self._closing_fut

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self.wait_closed()
        self._eof_sent = True

    @asyncio.coroutine
    def receive_msg(self):
        if self._reader is None:
            raise RuntimeError('Call .start() first')
        while True:
            try:
                msg = yield from self._reader.read()
            except Exception as exc:
                self._closing_fut.set_exception(exc)
                raise

            if msg.tp == MSG_CLOSE:
                if self._closing:
                    exc = WSClientDisconnectedError(msg.data, msg.extra)
                    self._closing_fut.set_exception(exc)
                    raise exc
                else:
                    self._closing = True
                    self._writer.close(msg.data, msg.extra)
                    yield from self.drain()
                    exc = WSClientDisconnectedError(msg.data, msg.extra)
                    self._closing_fut.set_exception(exc)
                    raise exc
            elif not self._closing:
                if msg.tp == MSG_PING:
                    self._writer.pong(msg.data)
                elif msg.tp in (MSG_TEXT, MSG_BINARY):
                    return msg

    @asyncio.coroutine
    def receive_str(self):
        msg = yield from self.receive_msg()
        if msg.tp != MSG_TEXT:
            raise TypeError(
                "Received message {}:{!r} is not str".format(msg.tp, msg.data))
        return msg.data

    @asyncio.coroutine
    def receive_bytes(self):
        msg = yield from self.receive_msg()
        if msg.tp != MSG_BINARY:
            raise TypeError(
                "Received message {}:{!r} is not bytes".format(msg.tp,
                                                               msg.data))
        return msg.data

    def write(self, data):
        raise RuntimeError("Cannot call .write() for websocket")


############################################################
# HTTP Exceptions
############################################################

class HTTPException(Response, Exception):

    # You should set in subclasses:
    # status = 200

    status_code = None

    def __init__(self, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        Response.__init__(self, status=self.status_code,
                          headers=headers, reason=reason,
                          body=body, text=text, content_type=content_type)
        Exception.__init__(self, self.reason)
        if self.body is None:
            self.text = "{}: {}".format(self.status, self.reason)


class HTTPError(HTTPException):
    """Base class for exceptions with status codes in the 400s and 500s."""


class HTTPRedirection(HTTPException):
    """Base class for exceptions with status codes in the 300s."""


class HTTPSuccessful(HTTPException):
    """Base class for exceptions with status codes in the 200s."""


class HTTPOk(HTTPSuccessful):
    status_code = 200


class HTTPCreated(HTTPSuccessful):
    status_code = 201


class HTTPAccepted(HTTPSuccessful):
    status_code = 202


class HTTPNonAuthoritativeInformation(HTTPSuccessful):
    status_code = 203


class HTTPNoContent(HTTPSuccessful):
    status_code = 204


class HTTPResetContent(HTTPSuccessful):
    status_code = 205


class HTTPPartialContent(HTTPSuccessful):
    status_code = 206


############################################################
# 3xx redirection
############################################################


class _HTTPMove(HTTPRedirection):

    def __init__(self, location, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        if not location:
            raise ValueError("HTTP redirects need a location to redirect to.")
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Location'] = location
        self.location = location


class HTTPMultipleChoices(_HTTPMove):
    status_code = 300


class HTTPMovedPermanently(_HTTPMove):
    status_code = 301


class HTTPFound(_HTTPMove):
    status_code = 302


# This one is safe after a POST (the redirected location will be
# retrieved with GET):
class HTTPSeeOther(_HTTPMove):
    status_code = 303


class HTTPNotModified(HTTPRedirection):
    # FIXME: this should include a date or etag header
    status_code = 304


class HTTPUseProxy(_HTTPMove):
    # Not a move, but looks a little like one
    status_code = 305


class HTTPTemporaryRedirect(_HTTPMove):
    status_code = 307


############################################################
# 4xx client error
############################################################


class HTTPClientError(HTTPError):
    pass


class HTTPBadRequest(HTTPClientError):
    status_code = 400


class HTTPUnauthorized(HTTPClientError):
    status_code = 401


class HTTPPaymentRequired(HTTPClientError):
    status_code = 402


class HTTPForbidden(HTTPClientError):
    status_code = 403


class HTTPNotFound(HTTPClientError):
    status_code = 404


class HTTPMethodNotAllowed(HTTPClientError):
    status_code = 405

    def __init__(self, method, allowed_methods, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        allow = ','.join(sorted(allowed_methods))
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Allow'] = allow
        self.allowed_methods = allowed_methods
        self.method = method.upper()


class HTTPNotAcceptable(HTTPClientError):
    status_code = 406


class HTTPProxyAuthenticationRequired(HTTPClientError):
    status_code = 407


class HTTPRequestTimeout(HTTPClientError):
    status_code = 408


class HTTPConflict(HTTPClientError):
    status_code = 409


class HTTPGone(HTTPClientError):
    status_code = 410


class HTTPLengthRequired(HTTPClientError):
    status_code = 411


class HTTPPreconditionFailed(HTTPClientError):
    status_code = 412


class HTTPRequestEntityTooLarge(HTTPClientError):
    status_code = 413


class HTTPRequestURITooLong(HTTPClientError):
    status_code = 414


class HTTPUnsupportedMediaType(HTTPClientError):
    status_code = 415


class HTTPRequestRangeNotSatisfiable(HTTPClientError):
    status_code = 416


class HTTPExpectationFailed(HTTPClientError):
    status_code = 417


############################################################
# 5xx Server Error
############################################################
#  Response status codes beginning with the digit "5" indicate cases in
#  which the server is aware that it has erred or is incapable of
#  performing the request. Except when responding to a HEAD request, the
#  server SHOULD include an entity containing an explanation of the error
#  situation, and whether it is a temporary or permanent condition. User
#  agents SHOULD display any included entity to the user. These response
#  codes are applicable to any request method.


class HTTPServerError(HTTPError):
    pass


class HTTPInternalServerError(HTTPServerError):
    status_code = 500


class HTTPNotImplemented(HTTPServerError):
    status_code = 501


class HTTPBadGateway(HTTPServerError):
    status_code = 502


class HTTPServiceUnavailable(HTTPServerError):
    status_code = 503


class HTTPGatewayTimeout(HTTPServerError):
    status_code = 504


class HTTPVersionNotSupported(HTTPServerError):
    status_code = 505


############################################################
# UrlDispatcher implementation
############################################################


class UrlMappingMatchInfo(dict, AbstractMatchInfo):

    def __init__(self, match_dict, route):
        super().__init__(match_dict)
        self._route = route

    @property
    def handler(self):
        return self._route.handler

    @property
    def route(self):
        return self._route

    def __repr__(self):
        return "<MatchInfo {}: {}>".format(super().__repr__(), self._route)


@asyncio.coroutine
def defaultExpectHandler(request):
    """Default handler for Except: 100-continue"""
    if request.version == HttpVersion11:
        request.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")


class Route(metaclass=abc.ABCMeta):

    def __init__(self, method, handler, name, expect_handler=None):
        if expect_handler is None:
            expect_handler = defaultExpectHandler
        assert asyncio.iscoroutinefunction(expect_handler), \
            'Coroutine is expected, got {!r}'.format(expect_handler)

        self._method = method
        self._handler = handler
        self._name = name
        self._expect_handler = expect_handler

    @property
    def method(self):
        return self._method

    @property
    def handler(self):
        return self._handler

    @property
    def name(self):
        return self._name

    @abc.abstractmethod
    def match(self, path):
        """Return dict with info for given path or
        None if route cannot process path."""

    @abc.abstractmethod
    def url(self, **kwargs):
        """Construct url for route with additional params."""

    @asyncio.coroutine
    def handle_expect_header(self, request):
        return (yield from self._expect_handler(request))

    @staticmethod
    def _append_query(url, query):
        if query is not None:
            return url + "?" + urlencode(query)
        else:
            return url


class PlainRoute(Route):

    def __init__(self, method, handler, name, path, expect_handler=None):
        super().__init__(method, handler, name, expect_handler=expect_handler)
        self._path = path

    def match(self, path):
        # string comparison is about 10 times faster than regexp matching
        if self._path == path:
            return {}
        else:
            return None

    def url(self, *, query=None):
        return self._append_query(self._path, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<PlainRoute {name}[{method}] {path} -> {handler!r}".format(
            name=name, method=self.method, path=self._path,
            handler=self.handler)


class DynamicRoute(Route):

    def __init__(self, method, handler, name, pattern, formatter,
                 expect_handler=None):
        super().__init__(method, handler, name, expect_handler=expect_handler)
        self._pattern = pattern
        self._formatter = formatter

    def match(self, path):
        match = self._pattern.match(path)
        if match is None:
            return None
        else:
            return match.groupdict()

    def url(self, *, parts, query=None):
        url = self._formatter.format_map(parts)
        return self._append_query(url, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return ("<DynamicRoute {name}[{method}] {formatter} -> {handler!r}"
                .format(name=name, method=self.method,
                        formatter=self._formatter, handler=self.handler))


class StaticRoute(Route):

    limit = 8192

    def __init__(self, name, prefix, directory, expect_handler=None):
        assert prefix.startswith('/'), prefix
        assert prefix.endswith('/'), prefix
        super().__init__(
            'GET', self.handle, name, expect_handler=expect_handler)
        self._prefix = prefix
        self._prefix_len = len(self._prefix)
        self._directory = directory

    def match(self, path):
        if not path.startswith(self._prefix):
            return None
        return {'filename': path[self._prefix_len:]}

    def url(self, *, filename, query=None):
        while filename.startswith('/'):
            filename = filename[1:]
        url = self._prefix + filename
        return self._append_query(url, query)

    @asyncio.coroutine
    def handle(self, request):
        resp = StreamResponse()
        filename = request.match_info['filename']
        filepath = os.path.join(self._directory, filename)
        if '..' in filename:
            raise HTTPNotFound()
        if not os.path.exists(filepath) or not os.path.isfile(filepath):
            raise HTTPNotFound()

        ct = mimetypes.guess_type(filename)[0]
        if not ct:
            ct = 'application/octet-stream'
        resp.content_type = ct

        file_size = os.stat(filepath).st_size
        single_chunk = file_size < self.limit

        if single_chunk:
            resp.content_length = file_size
        resp.start(request)

        with open(filepath, 'rb') as f:
            chunk = f.read(self.limit)
            if single_chunk:
                resp.write(chunk)
            else:
                while chunk:
                    resp.write(chunk)
                    chunk = f.read(self.limit)

        return resp

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<StaticRoute {name}[{method}] {path} -> {directory!r}".format(
            name=name, method=self.method, path=self._prefix,
            directory=self._directory)


class _NotFoundMatchInfo(UrlMappingMatchInfo):

    def __init__(self):
        super().__init__({}, None)

    @property
    def handler(self):
        return self._not_found

    @property
    def route(self):
        return None

    @asyncio.coroutine
    def _not_found(self, request):
        raise HTTPNotFound()

    def __repr__(self):
        return "<MatchInfo: not found>"


class _MethodNotAllowedMatchInfo(UrlMappingMatchInfo):

    def __init__(self, method, allowed_methods):
        super().__init__({}, None)
        self._method = method
        self._allowed_methods = allowed_methods

    @property
    def handler(self):
        return self._not_allowed

    @property
    def route(self):
        return None

    @asyncio.coroutine
    def _not_allowed(self, request):
        raise HTTPMethodNotAllowed(self._method, self._allowed_methods)

    def __repr__(self):
        return ("<MatchInfo: method {} is not allowed (allowed methods: {}>"
                .format(self._method,
                        ', '.join(sorted(self._allowed_methods))))


class UrlDispatcher(AbstractRouter, collections.abc.Mapping):

    DYN = re.compile(r'^\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*)\}$')
    DYN_WITH_RE = re.compile(
        r'^\{(?P<var>[a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\}$')
    GOOD = r'[^{}/]+'
    PLAIN = re.compile('^' + GOOD + '$')

    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}

    def __init__(self):
        super().__init__()
        self._urls = []
        self._routes = {}

    @asyncio.coroutine
    def resolve(self, request):
        path = request.path
        method = request.method
        allowed_methods = set()
        for route in self._urls:
            match_dict = route.match(path)
            if match_dict is None:
                continue
            route_method = route.method
            if route_method != method:
                allowed_methods.add(route_method)
            else:
                return UrlMappingMatchInfo(match_dict, route)
        else:
            if allowed_methods:
                return _MethodNotAllowedMatchInfo(method, allowed_methods)
            else:
                return _NotFoundMatchInfo()

    def __iter__(self):
        return iter(self._routes)

    def __len__(self):
        return len(self._routes)

    def __contains__(self, name):
        return name in self._routes

    def __getitem__(self, name):
        return self._routes[name]

    def _register_endpoint(self, route):
        name = route.name
        if name is not None:
            if name in self._routes:
                raise ValueError('Duplicate {!r}, '
                                 'already handled by {!r}'
                                 .format(name, self._routes[name]))
            else:
                self._routes[name] = route
        self._urls.append(route)

    def add_route(self, method, path, handler,
                  *, name=None, expect_handler=None):

        assert callable(handler), handler
        if not asyncio.iscoroutinefunction(handler):
            handler = asyncio.coroutine(handler)
        method = method.upper()
        assert method in self.METHODS, method

        if isinstance(path, retype):
            route = DynamicRoute(
                method, handler, name, path,
                path.pattern, expect_handler=expect_handler)

            self._register_endpoint(route)
            return route

        assert path.startswith('/')
        parts = []
        factory = PlainRoute
        format_parts = []
        for part in path.split('/'):
            if not part:
                continue
            match = self.DYN.match(part)
            if match:
                parts.append('(?P<' + match.group('var') + '>' +
                             self.GOOD + ')')
                factory = DynamicRoute
                format_parts.append('{'+match.group('var')+'}')
                continue

            match = self.DYN_WITH_RE.match(part)
            if match:
                parts.append('(?P<' + match.group('var') + '>' +
                             match.group('re') + ')')
                factory = DynamicRoute
                format_parts.append('{'+match.group('var')+'}')
                continue
            if self.PLAIN.match(part):
                parts.append(re.escape(part))
                format_parts.append(part)
                continue
            raise ValueError("Invalid path '{}'['{}']".format(path, part))
        if factory is PlainRoute:
            route = PlainRoute(
                method, handler, name, path, expect_handler=expect_handler)
        else:
            pattern = '/' + '/'.join(parts)
            formatter = '/' + '/'.join(format_parts)
            if path.endswith('/') and pattern != '/':
                pattern += '/'
                formatter += '/'
            try:
                compiled = re.compile('^' + pattern + '$')
            except re.error as exc:
                raise ValueError(
                    "Bad pattern '{}': {}".format(pattern, exc)) from None
            route = DynamicRoute(
                method, handler, name, compiled,
                formatter, expect_handler=expect_handler)

        self._register_endpoint(route)
        return route

    def add_static(self, prefix, path, *, name=None, expect_handler=None):
        """
        Adds static files view
        :param prefix - url prefix
        :param path - folder with files
        """
        assert prefix.startswith('/')
        assert os.path.isdir(path), 'Path does not directory %s' % path
        path = os.path.abspath(path)
        if not prefix.endswith('/'):
            prefix += '/'
        route = StaticRoute(name, prefix, path, expect_handler=expect_handler)
        self._register_endpoint(route)
        return route


############################################################
# Application implementation
############################################################


class RequestHandler(ServerHttpProtocol):

    def __init__(self, manager, app, router, **kwargs):
        super().__init__(**kwargs)

        self._manager = manager
        self._app = app
        self._router = router
        self._middlewares = app.middlewares

    def connection_made(self, transport):
        super().connection_made(transport)

        self._manager.connection_made(self, transport)

    def connection_lost(self, exc):
        self._manager.connection_lost(self, exc)

        super().connection_lost(exc)

    @asyncio.coroutine
    def handle_request(self, message, payload):
        now = self._loop.time()

        app = self._app
        request = Request(app, message, payload,
                          self.transport, self.reader, self.writer)
        try:
            match_info = yield from self._router.resolve(request)

            assert isinstance(match_info, AbstractMatchInfo), match_info

            resp = None
            request._match_info = match_info
            if request.headers.get(hdrs.EXPECT, '').lower() == "100-continue":
                resp = (
                    yield from match_info.route.handle_expect_header(request))

            if resp is None:
                handler = match_info.handler
                for factory in reversed(self._middlewares):
                    handler = yield from factory(app, handler)
                resp = yield from handler(request)

            if not isinstance(resp, StreamResponse):
                raise RuntimeError(
                    ("Handler {!r} should return response instance, "
                     "got {!r} [middlewares {!r}]").format(
                         match_info.handler,
                         type(resp),
                         self._middlewares))
        except HTTPException as exc:
            resp = exc

        resp_msg = resp.start(request)
        yield from resp.write_eof()

        # notify server about keep-alive
        self.keep_alive(resp_msg.keep_alive())

        # log access
        self.log_access(message, None, resp_msg, self._loop.time() - now)


class RequestHandlerFactory:

    def __init__(self, app, router, *,
                 handler=RequestHandler, loop=None, **kwargs):
        self._app = app
        self._router = router
        self._handler = handler
        self._loop = loop
        self._connections = {}
        self._kwargs = kwargs
        self._kwargs.setdefault('logger', app.logger)

    @property
    def connections(self):
        return list(self._connections.keys())

    def connection_made(self, handler, transport):
        self._connections[handler] = transport

    def connection_lost(self, handler, exc=None):
        if handler in self._connections:
            del self._connections[handler]

    @asyncio.coroutine
    def finish_connections(self, timeout=None):
        for handler in self._connections.keys():
            handler.closing()

        @asyncio.coroutine
        def cleanup():
            sleep = 0.05
            while self._connections:
                yield from asyncio.sleep(sleep, loop=self._loop)
                if sleep < 5:
                    sleep = sleep * 2

        if timeout:
            try:
                yield from asyncio.wait_for(
                    cleanup(), timeout, loop=self._loop)
            except asyncio.TimeoutError:
                self._app.logger.warning(
                    "Not all connections are closed (pending: %d)",
                    len(self._connections))

        for transport in self._connections.values():
            transport.close()

        self._connections.clear()

    def __call__(self):
        return self._handler(
            self, self._app, self._router, loop=self._loop, **self._kwargs)


class Application(dict):

    def __init__(self, *, logger=web_logger, loop=None,
                 router=None, handler_factory=RequestHandlerFactory,
                 middlewares=()):
        if loop is None:
            loop = asyncio.get_event_loop()
        if router is None:
            router = UrlDispatcher()
        assert isinstance(router, AbstractRouter), router

        self._router = router
        self._handler_factory = handler_factory
        self._finish_callbacks = []
        self._loop = loop
        self.logger = logger

        for factory in middlewares:
            assert asyncio.iscoroutinefunction(factory), factory
        self._middlewares = tuple(middlewares)

    @property
    def router(self):
        return self._router

    @property
    def loop(self):
        return self._loop

    @property
    def middlewares(self):
        return self._middlewares

    def make_handler(self, **kwargs):
        return self._handler_factory(
            self, self.router, loop=self.loop, **kwargs)

    @asyncio.coroutine
    def finish(self):
        callbacks = self._finish_callbacks
        self._finish_callbacks = []

        for (cb, args, kwargs) in callbacks:
            try:
                res = cb(self, *args, **kwargs)
                if (asyncio.iscoroutine(res) or
                        isinstance(res, asyncio.Future)):
                    yield from res
            except Exception as exc:
                self._loop.call_exception_handler({
                    'message': "Error in finish callback",
                    'exception': exc,
                    'application': self,
                })

    def register_on_finish(self, func, *args, **kwargs):
        self._finish_callbacks.insert(0, (func, args, kwargs))

    def __call__(self):
        """gunicorn compatibility"""
        return self

    def __repr__(self):
        return "<Application>"

```
--- 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