prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Bug: openapi render for multiple tags isn't consistent
  **Describe the bug**
  When the openapi renders tags from both a controller and a route it is not deterministic. This may not be a bug? But it surprised me so thought I'd raise it.
  
  I'm unsure if I'm doing something crazy but for a project, we check in the generated json openapi schema so we can browse the API live in gitlab. I've recently added a tag to both a controller and a route in it. But because the order of the tags isn't consistent they are going to keep flip flopping as we have a pre-commit that generates the json to make sure it's up to date. I hope that ramble makes sense...
  
  **To Reproduce**
  ```python
  from typing import Dict                                           
                                                                    
  from starlite import Starlite, Controller, get                    
                                                                    
  class TestController(Controller):                                 
      tags = ["a"]                                                  
                                                                    
      @get("/", tags=["b"])                                         
      def hello_world(self) -> Dict[str, str]:                      
          """Handler function that returns a greeting dictionary."""
          return {"hello": "world"}                                 
                                                                    
                                                                    
  app = Starlite(route_handlers=[TestController])                   
  print(app.openapi_schema.paths["/"].get.tags)                     
  ```
  If you run that multiple times, you will see you get either:
  ```python
  ['a', 'b']
  ```
  or
  ```python
  ['b', 'a']
  ```
  
  **Additional context**
  I believe the problem is [here](https://github.com/starlite-api/starlite/blob/835749112e8364c1516f45973c924774aca22ca9/starlite/openapi/path_item.py#L59) as it forces construction of a new set. Sorting them before returning would be viable as there shouldn't be _too many_ tags and it's a one time thing I believe?
  
  But as I said, it may not be a problem you care about as I could be doing something silly.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `starlite/openapi/path_item.py`
  Content:
  ```
  1 from inspect import cleandoc
  2 from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, cast
  3 
  4 from pydantic_openapi_schema.v3_1_0.operation import Operation
  5 from pydantic_openapi_schema.v3_1_0.path_item import PathItem
  6 
  7 from starlite.openapi.parameters import create_parameter_for_handler
  8 from starlite.openapi.request_body import create_request_body
  9 from starlite.openapi.responses import create_responses
  10 from starlite.utils.helpers import unwrap_partial
  11 
  12 if TYPE_CHECKING:
  13     from pydantic import BaseModel
  14     from pydantic_openapi_schema.v3_1_0 import SecurityRequirement
  15 
  16     from starlite.handlers import HTTPRouteHandler
  17     from starlite.plugins.base import PluginProtocol
  18     from starlite.routes import HTTPRoute
  19 
  20 
  21 def get_description_for_handler(route_handler: "HTTPRouteHandler", use_handler_docstrings: bool) -> Optional[str]:
  22     """Produce the operation description for a route handler, either by using the description value if provided,
  23 
  24     or the docstring - if config is enabled.
  25 
  26     Args:
  27         route_handler: A route handler instance.
  28         use_handler_docstrings: If `True` and `route_handler.description` is `None` returns docstring of wrapped
  29             handler function.
  30 
  31     Returns:
  32         An optional description string
  33     """
  34     handler_description = route_handler.description
  35     if handler_description is None and use_handler_docstrings:
  36         fn = unwrap_partial(route_handler.fn.value)
  37         return cleandoc(fn.__doc__) if fn.__doc__ else None
  38     return handler_description
  39 
  40 
  41 def extract_layered_values(
  42     route_handler: "HTTPRouteHandler",
  43 ) -> Tuple[Optional[List[str]], Optional[List[Dict[str, List[str]]]]]:
  44     """Extract the tags and security values from the route handler layers.
  45 
  46     Args:
  47         route_handler: A Route Handler instance.
  48 
  49     Returns:
  50         A tuple of optional lists.
  51     """
  52     tags: List[str] = []
  53     security: List["SecurityRequirement"] = []
  54     for layer in route_handler.ownership_layers:
  55         if layer.tags:
  56             tags.extend(layer.tags)
  57         if layer.security:
  58             security.extend(layer.security)
  59     return list(set(tags)) if tags else None, security or None
  60 
  61 
  62 def create_path_item(
  63     route: "HTTPRoute", create_examples: bool, plugins: List["PluginProtocol"], use_handler_docstrings: bool
  64 ) -> PathItem:
  65     """Create a PathItem model for the given route parsing all http_methods into Operation Models."""
  66     path_item = PathItem()
  67     for http_method, handler_tuple in route.route_handler_map.items():
  68         route_handler, _ = handler_tuple
  69         if route_handler.include_in_schema:
  70             handler_fields = cast("BaseModel", route_handler.signature_model).__fields__
  71             parameters = (
  72                 create_parameter_for_handler(
  73                     route_handler=route_handler,
  74                     handler_fields=handler_fields,
  75                     path_parameters=route.path_parameters,
  76                     generate_examples=create_examples,
  77                 )
  78                 or None
  79             )
  80             raises_validation_error = bool("data" in handler_fields or path_item.parameters or parameters)
  81             handler_name = unwrap_partial(route_handler.handler_name).replace("_", " ").title()
  82             request_body = None
  83             if "data" in handler_fields:
  84                 request_body = create_request_body(
  85                     field=handler_fields["data"], generate_examples=create_examples, plugins=plugins
  86                 )
  87 
  88             tags, security = extract_layered_values(route_handler)
  89             operation = Operation(
  90                 operationId=route_handler.operation_id or handler_name,
  91                 tags=tags,
  92                 summary=route_handler.summary,
  93                 description=get_description_for_handler(route_handler, use_handler_docstrings),
  94                 deprecated=route_handler.deprecated,
  95                 responses=create_responses(
  96                     route_handler=route_handler,
  97                     raises_validation_error=raises_validation_error,
  98                     generate_examples=create_examples,
  99                     plugins=plugins,
  100                 ),
  101                 requestBody=request_body,
  102                 parameters=parameters,  # type: ignore[arg-type]
  103                 security=security,
  104             )
  105             setattr(path_item, http_method.lower(), operation)
  106     return path_item
  107
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/starlite/openapi/path_item.py b/starlite/openapi/path_item.py
  --- a/starlite/openapi/path_item.py
  +++ b/starlite/openapi/path_item.py
  @@ -56,7 +56,7 @@
               tags.extend(layer.tags)
           if layer.security:
               security.extend(layer.security)
  -    return list(set(tags)) if tags else None, security or None
  +    return sorted(set(tags)) if tags else None, security or None
   
   
   def create_path_item(
  
verification_info: {"golden_diff": "diff --git a/starlite/openapi/path_item.py b/starlite/openapi/path_item.py\n--- a/starlite/openapi/path_item.py\n+++ b/starlite/openapi/path_item.py\n@@ -56,7 +56,7 @@\n             tags.extend(layer.tags)\n         if layer.security:\n             security.extend(layer.security)\n-    return list(set(tags)) if tags else None, security or None\n+    return sorted(set(tags)) if tags else None, security or None\n \n \n def create_path_item(\n", "issue": "Bug: openapi render for multiple tags isn't consistent\n**Describe the bug**\r\nWhen the openapi renders tags from both a controller and a route it is not deterministic. This may not be a bug? But it surprised me so thought I'd raise it.\r\n\r\nI'm unsure if I'm doing something crazy but for a project, we check in the generated json openapi schema so we can browse the API live in gitlab. I've recently added a tag to both a controller and a route in it. But because the order of the tags isn't consistent they are going to keep flip flopping as we have a pre-commit that generates the json to make sure it's up to date. I hope that ramble makes sense...\r\n\r\n**To Reproduce**\r\n```python\r\nfrom typing import Dict                                           \r\n                                                                  \r\nfrom starlite import Starlite, Controller, get                    \r\n                                                                  \r\nclass TestController(Controller):                                 \r\n    tags = [\"a\"]                                                  \r\n                                                                  \r\n    @get(\"/\", tags=[\"b\"])                                         \r\n    def hello_world(self) -> Dict[str, str]:                      \r\n        \"\"\"Handler function that returns a greeting dictionary.\"\"\"\r\n        return {\"hello\": \"world\"}                                 \r\n                                                                  \r\n                                                                  \r\napp = Starlite(route_handlers=[TestController])                   \r\nprint(app.openapi_schema.paths[\"/\"].get.tags)                     \r\n```\r\nIf you run that multiple times, you will see you get either:\r\n```python\r\n['a', 'b']\r\n```\r\nor\r\n```python\r\n['b', 'a']\r\n```\r\n\r\n**Additional context**\r\nI believe the problem is [here](https://github.com/starlite-api/starlite/blob/835749112e8364c1516f45973c924774aca22ca9/starlite/openapi/path_item.py#L59) as it forces construction of a new set. Sorting them before returning would be viable as there shouldn't be _too many_ tags and it's a one time thing I believe?\r\n\r\nBut as I said, it may not be a problem you care about as I could be doing something silly.\r\n\n", "before_files": [{"content": "from inspect import cleandoc\nfrom typing import TYPE_CHECKING, Dict, List, Optional, Tuple, cast\n\nfrom pydantic_openapi_schema.v3_1_0.operation import Operation\nfrom pydantic_openapi_schema.v3_1_0.path_item import PathItem\n\nfrom starlite.openapi.parameters import create_parameter_for_handler\nfrom starlite.openapi.request_body import create_request_body\nfrom starlite.openapi.responses import create_responses\nfrom starlite.utils.helpers import unwrap_partial\n\nif TYPE_CHECKING:\n    from pydantic import BaseModel\n    from pydantic_openapi_schema.v3_1_0 import SecurityRequirement\n\n    from starlite.handlers import HTTPRouteHandler\n    from starlite.plugins.base import PluginProtocol\n    from starlite.routes import HTTPRoute\n\n\ndef get_description_for_handler(route_handler: \"HTTPRouteHandler\", use_handler_docstrings: bool) -> Optional[str]:\n    \"\"\"Produce the operation description for a route handler, either by using the description value if provided,\n\n    or the docstring - if config is enabled.\n\n    Args:\n        route_handler: A route handler instance.\n        use_handler_docstrings: If `True` and `route_handler.description` is `None` returns docstring of wrapped\n            handler function.\n\n    Returns:\n        An optional description string\n    \"\"\"\n    handler_description = route_handler.description\n    if handler_description is None and use_handler_docstrings:\n        fn = unwrap_partial(route_handler.fn.value)\n        return cleandoc(fn.__doc__) if fn.__doc__ else None\n    return handler_description\n\n\ndef extract_layered_values(\n    route_handler: \"HTTPRouteHandler\",\n) -> Tuple[Optional[List[str]], Optional[List[Dict[str, List[str]]]]]:\n    \"\"\"Extract the tags and security values from the route handler layers.\n\n    Args:\n        route_handler: A Route Handler instance.\n\n    Returns:\n        A tuple of optional lists.\n    \"\"\"\n    tags: List[str] = []\n    security: List[\"SecurityRequirement\"] = []\n    for layer in route_handler.ownership_layers:\n        if layer.tags:\n            tags.extend(layer.tags)\n        if layer.security:\n            security.extend(layer.security)\n    return list(set(tags)) if tags else None, security or None\n\n\ndef create_path_item(\n    route: \"HTTPRoute\", create_examples: bool, plugins: List[\"PluginProtocol\"], use_handler_docstrings: bool\n) -> PathItem:\n    \"\"\"Create a PathItem model for the given route parsing all http_methods into Operation Models.\"\"\"\n    path_item = PathItem()\n    for http_method, handler_tuple in route.route_handler_map.items():\n        route_handler, _ = handler_tuple\n        if route_handler.include_in_schema:\n            handler_fields = cast(\"BaseModel\", route_handler.signature_model).__fields__\n            parameters = (\n                create_parameter_for_handler(\n                    route_handler=route_handler,\n                    handler_fields=handler_fields,\n                    path_parameters=route.path_parameters,\n                    generate_examples=create_examples,\n                )\n                or None\n            )\n            raises_validation_error = bool(\"data\" in handler_fields or path_item.parameters or parameters)\n            handler_name = unwrap_partial(route_handler.handler_name).replace(\"_\", \" \").title()\n            request_body = None\n            if \"data\" in handler_fields:\n                request_body = create_request_body(\n                    field=handler_fields[\"data\"], generate_examples=create_examples, plugins=plugins\n                )\n\n            tags, security = extract_layered_values(route_handler)\n            operation = Operation(\n                operationId=route_handler.operation_id or handler_name,\n                tags=tags,\n                summary=route_handler.summary,\n                description=get_description_for_handler(route_handler, use_handler_docstrings),\n                deprecated=route_handler.deprecated,\n                responses=create_responses(\n                    route_handler=route_handler,\n                    raises_validation_error=raises_validation_error,\n                    generate_examples=create_examples,\n                    plugins=plugins,\n                ),\n                requestBody=request_body,\n                parameters=parameters,  # type: ignore[arg-type]\n                security=security,\n            )\n            setattr(path_item, http_method.lower(), operation)\n    return path_item\n", "path": "starlite/openapi/path_item.py"}], "after_files": [{"content": "from inspect import cleandoc\nfrom typing import TYPE_CHECKING, Dict, List, Optional, Tuple, cast\n\nfrom pydantic_openapi_schema.v3_1_0.operation import Operation\nfrom pydantic_openapi_schema.v3_1_0.path_item import PathItem\n\nfrom starlite.openapi.parameters import create_parameter_for_handler\nfrom starlite.openapi.request_body import create_request_body\nfrom starlite.openapi.responses import create_responses\nfrom starlite.utils.helpers import unwrap_partial\n\nif TYPE_CHECKING:\n    from pydantic import BaseModel\n    from pydantic_openapi_schema.v3_1_0 import SecurityRequirement\n\n    from starlite.handlers import HTTPRouteHandler\n    from starlite.plugins.base import PluginProtocol\n    from starlite.routes import HTTPRoute\n\n\ndef get_description_for_handler(route_handler: \"HTTPRouteHandler\", use_handler_docstrings: bool) -> Optional[str]:\n    \"\"\"Produce the operation description for a route handler, either by using the description value if provided,\n\n    or the docstring - if config is enabled.\n\n    Args:\n        route_handler: A route handler instance.\n        use_handler_docstrings: If `True` and `route_handler.description` is `None` returns docstring of wrapped\n            handler function.\n\n    Returns:\n        An optional description string\n    \"\"\"\n    handler_description = route_handler.description\n    if handler_description is None and use_handler_docstrings:\n        fn = unwrap_partial(route_handler.fn.value)\n        return cleandoc(fn.__doc__) if fn.__doc__ else None\n    return handler_description\n\n\ndef extract_layered_values(\n    route_handler: \"HTTPRouteHandler\",\n) -> Tuple[Optional[List[str]], Optional[List[Dict[str, List[str]]]]]:\n    \"\"\"Extract the tags and security values from the route handler layers.\n\n    Args:\n        route_handler: A Route Handler instance.\n\n    Returns:\n        A tuple of optional lists.\n    \"\"\"\n    tags: List[str] = []\n    security: List[\"SecurityRequirement\"] = []\n    for layer in route_handler.ownership_layers:\n        if layer.tags:\n            tags.extend(layer.tags)\n        if layer.security:\n            security.extend(layer.security)\n    return sorted(set(tags)) if tags else None, security or None\n\n\ndef create_path_item(\n    route: \"HTTPRoute\", create_examples: bool, plugins: List[\"PluginProtocol\"], use_handler_docstrings: bool\n) -> PathItem:\n    \"\"\"Create a PathItem model for the given route parsing all http_methods into Operation Models.\"\"\"\n    path_item = PathItem()\n    for http_method, handler_tuple in route.route_handler_map.items():\n        route_handler, _ = handler_tuple\n        if route_handler.include_in_schema:\n            handler_fields = cast(\"BaseModel\", route_handler.signature_model).__fields__\n            parameters = (\n                create_parameter_for_handler(\n                    route_handler=route_handler,\n                    handler_fields=handler_fields,\n                    path_parameters=route.path_parameters,\n                    generate_examples=create_examples,\n                )\n                or None\n            )\n            raises_validation_error = bool(\"data\" in handler_fields or path_item.parameters or parameters)\n            handler_name = unwrap_partial(route_handler.handler_name).replace(\"_\", \" \").title()\n            request_body = None\n            if \"data\" in handler_fields:\n                request_body = create_request_body(\n                    field=handler_fields[\"data\"], generate_examples=create_examples, plugins=plugins\n                )\n\n            tags, security = extract_layered_values(route_handler)\n            operation = Operation(\n                operationId=route_handler.operation_id or handler_name,\n                tags=tags,\n                summary=route_handler.summary,\n                description=get_description_for_handler(route_handler, use_handler_docstrings),\n                deprecated=route_handler.deprecated,\n                responses=create_responses(\n                    route_handler=route_handler,\n                    raises_validation_error=raises_validation_error,\n                    generate_examples=create_examples,\n                    plugins=plugins,\n                ),\n                requestBody=request_body,\n                parameters=parameters,  # type: ignore[arg-type]\n                security=security,\n            )\n            setattr(path_item, http_method.lower(), operation)\n    return path_item\n", "path": "starlite/openapi/path_item.py"}]}