prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Endpoint path parameters type specification compatibility with OAS
  When specifying a convertor type for a path parameter like so:
  ```
  ...
  Route('/users/{user_id:int}', user, methods=["GET", "POST"])
  ...
  ```
  
  The OAS schema generated using `SchemaGenerator` interprets the whole portion within `{}`  eg. `'user_id:int'` as the required path parameter variable name, which then requires the parameter name in OAS to be `user_id:int` instead of just `user_id`.
  
  ![user_idint](https://user-images.githubusercontent.com/26809407/70369654-50312280-18eb-11ea-9089-53743115394c.png)
  
  I think the convertor is great in that a GET request to `/users/foo` will result in `404: NOT FOUND`. Of course, another option is to have a `try-except` clause to handle non-integer values within the method... but I was wondering what the community thinks about this and if this even should be supported by the `SchemaGenerator`.
  
  </issue>
  <code>
  [start of starlette/schemas.py]
  1 import inspect
  2 import typing
  3 
  4 from starlette.requests import Request
  5 from starlette.responses import Response
  6 from starlette.routing import BaseRoute, Mount, Route
  7 
  8 try:
  9     import yaml
  10 except ImportError:  # pragma: nocover
  11     yaml = None  # type: ignore
  12 
  13 
  14 class OpenAPIResponse(Response):
  15     media_type = "application/vnd.oai.openapi"
  16 
  17     def render(self, content: typing.Any) -> bytes:
  18         assert yaml is not None, "`pyyaml` must be installed to use OpenAPIResponse."
  19         assert isinstance(
  20             content, dict
  21         ), "The schema passed to OpenAPIResponse should be a dictionary."
  22         return yaml.dump(content, default_flow_style=False).encode("utf-8")
  23 
  24 
  25 class EndpointInfo(typing.NamedTuple):
  26     path: str
  27     http_method: str
  28     func: typing.Callable
  29 
  30 
  31 class BaseSchemaGenerator:
  32     def get_schema(self, routes: typing.List[BaseRoute]) -> dict:
  33         raise NotImplementedError()  # pragma: no cover
  34 
  35     def get_endpoints(
  36         self, routes: typing.List[BaseRoute]
  37     ) -> typing.List[EndpointInfo]:
  38         """
  39         Given the routes, yields the following information:
  40 
  41         - path
  42             eg: /users/
  43         - http_method
  44             one of 'get', 'post', 'put', 'patch', 'delete', 'options'
  45         - func
  46             method ready to extract the docstring
  47         """
  48         endpoints_info: list = []
  49 
  50         for route in routes:
  51             if isinstance(route, Mount):
  52                 routes = route.routes or []
  53                 sub_endpoints = [
  54                     EndpointInfo(
  55                         path="".join((route.path, sub_endpoint.path)),
  56                         http_method=sub_endpoint.http_method,
  57                         func=sub_endpoint.func,
  58                     )
  59                     for sub_endpoint in self.get_endpoints(routes)
  60                 ]
  61                 endpoints_info.extend(sub_endpoints)
  62 
  63             elif not isinstance(route, Route) or not route.include_in_schema:
  64                 continue
  65 
  66             elif inspect.isfunction(route.endpoint) or inspect.ismethod(route.endpoint):
  67                 for method in route.methods or ["GET"]:
  68                     if method == "HEAD":
  69                         continue
  70                     endpoints_info.append(
  71                         EndpointInfo(route.path, method.lower(), route.endpoint)
  72                     )
  73             else:
  74                 for method in ["get", "post", "put", "patch", "delete", "options"]:
  75                     if not hasattr(route.endpoint, method):
  76                         continue
  77                     func = getattr(route.endpoint, method)
  78                     endpoints_info.append(
  79                         EndpointInfo(route.path, method.lower(), func)
  80                     )
  81 
  82         return endpoints_info
  83 
  84     def parse_docstring(self, func_or_method: typing.Callable) -> dict:
  85         """
  86         Given a function, parse the docstring as YAML and return a dictionary of info.
  87         """
  88         docstring = func_or_method.__doc__
  89         if not docstring:
  90             return {}
  91 
  92         assert yaml is not None, "`pyyaml` must be installed to use parse_docstring."
  93 
  94         # We support having regular docstrings before the schema
  95         # definition. Here we return just the schema part from
  96         # the docstring.
  97         docstring = docstring.split("---")[-1]
  98 
  99         parsed = yaml.safe_load(docstring)
  100 
  101         if not isinstance(parsed, dict):
  102             # A regular docstring (not yaml formatted) can return
  103             # a simple string here, which wouldn't follow the schema.
  104             return {}
  105 
  106         return parsed
  107 
  108     def OpenAPIResponse(self, request: Request) -> Response:
  109         routes = request.app.routes
  110         schema = self.get_schema(routes=routes)
  111         return OpenAPIResponse(schema)
  112 
  113 
  114 class SchemaGenerator(BaseSchemaGenerator):
  115     def __init__(self, base_schema: dict) -> None:
  116         self.base_schema = base_schema
  117 
  118     def get_schema(self, routes: typing.List[BaseRoute]) -> dict:
  119         schema = dict(self.base_schema)
  120         schema.setdefault("paths", {})
  121         endpoints_info = self.get_endpoints(routes)
  122 
  123         for endpoint in endpoints_info:
  124 
  125             parsed = self.parse_docstring(endpoint.func)
  126 
  127             if not parsed:
  128                 continue
  129 
  130             if endpoint.path not in schema["paths"]:
  131                 schema["paths"][endpoint.path] = {}
  132 
  133             schema["paths"][endpoint.path][endpoint.http_method] = parsed
  134 
  135         return schema
  136
  [end of starlette/schemas.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/starlette/schemas.py b/starlette/schemas.py
  --- a/starlette/schemas.py
  +++ b/starlette/schemas.py
  @@ -1,4 +1,5 @@
   import inspect
  +import re
   import typing
   
   from starlette.requests import Request
  @@ -49,10 +50,11 @@
   
           for route in routes:
               if isinstance(route, Mount):
  +                path = self._remove_converter(route.path)
                   routes = route.routes or []
                   sub_endpoints = [
                       EndpointInfo(
  -                        path="".join((route.path, sub_endpoint.path)),
  +                        path="".join((path, sub_endpoint.path)),
                           http_method=sub_endpoint.http_method,
                           func=sub_endpoint.func,
                       )
  @@ -64,23 +66,32 @@
                   continue
   
               elif inspect.isfunction(route.endpoint) or inspect.ismethod(route.endpoint):
  +                path = self._remove_converter(route.path)
                   for method in route.methods or ["GET"]:
                       if method == "HEAD":
                           continue
                       endpoints_info.append(
  -                        EndpointInfo(route.path, method.lower(), route.endpoint)
  +                        EndpointInfo(path, method.lower(), route.endpoint)
                       )
               else:
  +                path = self._remove_converter(route.path)
                   for method in ["get", "post", "put", "patch", "delete", "options"]:
                       if not hasattr(route.endpoint, method):
                           continue
                       func = getattr(route.endpoint, method)
  -                    endpoints_info.append(
  -                        EndpointInfo(route.path, method.lower(), func)
  -                    )
  +                    endpoints_info.append(EndpointInfo(path, method.lower(), func))
   
           return endpoints_info
   
  +    def _remove_converter(self, path: str) -> str:
  +        """
  +        Remove the converter from the path.
  +        For example, a route like this:
  +            Route("/users/{id:int}", endpoint=get_user, methods=["GET"])
  +        Should be represented as `/users/{id}` in the OpenAPI schema.
  +        """
  +        return re.sub(r":\w+}", "}", path)
  +
       def parse_docstring(self, func_or_method: typing.Callable) -> dict:
           """
           Given a function, parse the docstring as YAML and return a dictionary of info.
  
verification_info: {"golden_diff": "diff --git a/starlette/schemas.py b/starlette/schemas.py\n--- a/starlette/schemas.py\n+++ b/starlette/schemas.py\n@@ -1,4 +1,5 @@\n import inspect\n+import re\n import typing\n \n from starlette.requests import Request\n@@ -49,10 +50,11 @@\n \n         for route in routes:\n             if isinstance(route, Mount):\n+                path = self._remove_converter(route.path)\n                 routes = route.routes or []\n                 sub_endpoints = [\n                     EndpointInfo(\n-                        path=\"\".join((route.path, sub_endpoint.path)),\n+                        path=\"\".join((path, sub_endpoint.path)),\n                         http_method=sub_endpoint.http_method,\n                         func=sub_endpoint.func,\n                     )\n@@ -64,23 +66,32 @@\n                 continue\n \n             elif inspect.isfunction(route.endpoint) or inspect.ismethod(route.endpoint):\n+                path = self._remove_converter(route.path)\n                 for method in route.methods or [\"GET\"]:\n                     if method == \"HEAD\":\n                         continue\n                     endpoints_info.append(\n-                        EndpointInfo(route.path, method.lower(), route.endpoint)\n+                        EndpointInfo(path, method.lower(), route.endpoint)\n                     )\n             else:\n+                path = self._remove_converter(route.path)\n                 for method in [\"get\", \"post\", \"put\", \"patch\", \"delete\", \"options\"]:\n                     if not hasattr(route.endpoint, method):\n                         continue\n                     func = getattr(route.endpoint, method)\n-                    endpoints_info.append(\n-                        EndpointInfo(route.path, method.lower(), func)\n-                    )\n+                    endpoints_info.append(EndpointInfo(path, method.lower(), func))\n \n         return endpoints_info\n \n+    def _remove_converter(self, path: str) -> str:\n+        \"\"\"\n+        Remove the converter from the path.\n+        For example, a route like this:\n+            Route(\"/users/{id:int}\", endpoint=get_user, methods=[\"GET\"])\n+        Should be represented as `/users/{id}` in the OpenAPI schema.\n+        \"\"\"\n+        return re.sub(r\":\\w+}\", \"}\", path)\n+\n     def parse_docstring(self, func_or_method: typing.Callable) -> dict:\n         \"\"\"\n         Given a function, parse the docstring as YAML and return a dictionary of info.\n", "issue": "Endpoint path parameters type specification compatibility with OAS\nWhen specifying a convertor type for a path parameter like so:\r\n```\r\n...\r\nRoute('/users/{user_id:int}', user, methods=[\"GET\", \"POST\"])\r\n...\r\n```\r\n\r\nThe OAS schema generated using `SchemaGenerator` interprets the whole portion within `{}`  eg. `'user_id:int'` as the required path parameter variable name, which then requires the parameter name in OAS to be `user_id:int` instead of just `user_id`.\r\n\r\n![user_idint](https://user-images.githubusercontent.com/26809407/70369654-50312280-18eb-11ea-9089-53743115394c.png)\r\n\r\nI think the convertor is great in that a GET request to `/users/foo` will result in `404: NOT FOUND`. Of course, another option is to have a `try-except` clause to handle non-integer values within the method... but I was wondering what the community thinks about this and if this even should be supported by the `SchemaGenerator`.\n", "before_files": [{"content": "import inspect\nimport typing\n\nfrom starlette.requests import Request\nfrom starlette.responses import Response\nfrom starlette.routing import BaseRoute, Mount, Route\n\ntry:\n    import yaml\nexcept ImportError:  # pragma: nocover\n    yaml = None  # type: ignore\n\n\nclass OpenAPIResponse(Response):\n    media_type = \"application/vnd.oai.openapi\"\n\n    def render(self, content: typing.Any) -> bytes:\n        assert yaml is not None, \"`pyyaml` must be installed to use OpenAPIResponse.\"\n        assert isinstance(\n            content, dict\n        ), \"The schema passed to OpenAPIResponse should be a dictionary.\"\n        return yaml.dump(content, default_flow_style=False).encode(\"utf-8\")\n\n\nclass EndpointInfo(typing.NamedTuple):\n    path: str\n    http_method: str\n    func: typing.Callable\n\n\nclass BaseSchemaGenerator:\n    def get_schema(self, routes: typing.List[BaseRoute]) -> dict:\n        raise NotImplementedError()  # pragma: no cover\n\n    def get_endpoints(\n        self, routes: typing.List[BaseRoute]\n    ) -> typing.List[EndpointInfo]:\n        \"\"\"\n        Given the routes, yields the following information:\n\n        - path\n            eg: /users/\n        - http_method\n            one of 'get', 'post', 'put', 'patch', 'delete', 'options'\n        - func\n            method ready to extract the docstring\n        \"\"\"\n        endpoints_info: list = []\n\n        for route in routes:\n            if isinstance(route, Mount):\n                routes = route.routes or []\n                sub_endpoints = [\n                    EndpointInfo(\n                        path=\"\".join((route.path, sub_endpoint.path)),\n                        http_method=sub_endpoint.http_method,\n                        func=sub_endpoint.func,\n                    )\n                    for sub_endpoint in self.get_endpoints(routes)\n                ]\n                endpoints_info.extend(sub_endpoints)\n\n            elif not isinstance(route, Route) or not route.include_in_schema:\n                continue\n\n            elif inspect.isfunction(route.endpoint) or inspect.ismethod(route.endpoint):\n                for method in route.methods or [\"GET\"]:\n                    if method == \"HEAD\":\n                        continue\n                    endpoints_info.append(\n                        EndpointInfo(route.path, method.lower(), route.endpoint)\n                    )\n            else:\n                for method in [\"get\", \"post\", \"put\", \"patch\", \"delete\", \"options\"]:\n                    if not hasattr(route.endpoint, method):\n                        continue\n                    func = getattr(route.endpoint, method)\n                    endpoints_info.append(\n                        EndpointInfo(route.path, method.lower(), func)\n                    )\n\n        return endpoints_info\n\n    def parse_docstring(self, func_or_method: typing.Callable) -> dict:\n        \"\"\"\n        Given a function, parse the docstring as YAML and return a dictionary of info.\n        \"\"\"\n        docstring = func_or_method.__doc__\n        if not docstring:\n            return {}\n\n        assert yaml is not None, \"`pyyaml` must be installed to use parse_docstring.\"\n\n        # We support having regular docstrings before the schema\n        # definition. Here we return just the schema part from\n        # the docstring.\n        docstring = docstring.split(\"---\")[-1]\n\n        parsed = yaml.safe_load(docstring)\n\n        if not isinstance(parsed, dict):\n            # A regular docstring (not yaml formatted) can return\n            # a simple string here, which wouldn't follow the schema.\n            return {}\n\n        return parsed\n\n    def OpenAPIResponse(self, request: Request) -> Response:\n        routes = request.app.routes\n        schema = self.get_schema(routes=routes)\n        return OpenAPIResponse(schema)\n\n\nclass SchemaGenerator(BaseSchemaGenerator):\n    def __init__(self, base_schema: dict) -> None:\n        self.base_schema = base_schema\n\n    def get_schema(self, routes: typing.List[BaseRoute]) -> dict:\n        schema = dict(self.base_schema)\n        schema.setdefault(\"paths\", {})\n        endpoints_info = self.get_endpoints(routes)\n\n        for endpoint in endpoints_info:\n\n            parsed = self.parse_docstring(endpoint.func)\n\n            if not parsed:\n                continue\n\n            if endpoint.path not in schema[\"paths\"]:\n                schema[\"paths\"][endpoint.path] = {}\n\n            schema[\"paths\"][endpoint.path][endpoint.http_method] = parsed\n\n        return schema\n", "path": "starlette/schemas.py"}]}