prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Python 3.1 classifier filtering is broken
  When [filtering by the `Programming Language :: Python :: 3.1` classifier on pypi.org][1], the results include packages which don't have that classifier - any package with a classifier matching `Programming Language :: Python :: 3.1*` is included. That is, packages for 3.10, 3.11, 3.12, and so on are included in the results.
  
  [1]: https://pypi.org/search/?q=&o=&c=Programming+Language+%3A%3A+Python+%3A%3A+3.1
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `warehouse/search/queries.py`
  Content:
  ```
  1 # Licensed under the Apache License, Version 2.0 (the "License");
  2 # you may not use this file except in compliance with the License.
  3 # You may obtain a copy of the License at
  4 #
  5 # http://www.apache.org/licenses/LICENSE-2.0
  6 #
  7 # Unless required by applicable law or agreed to in writing, software
  8 # distributed under the License is distributed on an "AS IS" BASIS,
  9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10 # See the License for the specific language governing permissions and
  11 # limitations under the License.
  12 
  13 import re
  14 
  15 from elasticsearch_dsl import Q
  16 
  17 SEARCH_FIELDS = [
  18     "author",
  19     "author_email",
  20     "description",
  21     "download_url",
  22     "home_page",
  23     "keywords",
  24     "license",
  25     "maintainer",
  26     "maintainer_email",
  27     "normalized_name",
  28     "platform",
  29     "summary",
  30 ]
  31 SEARCH_BOOSTS = {
  32     "name": 10,
  33     "normalized_name": 10,
  34     "description": 5,
  35     "keywords": 5,
  36     "summary": 5,
  37 }
  38 SEARCH_FILTER_ORDER = (
  39     "Framework",
  40     "Topic",
  41     "Development Status",
  42     "License",
  43     "Programming Language",
  44     "Operating System",
  45     "Environment",
  46     "Intended Audience",
  47     "Natural Language",
  48 )
  49 
  50 
  51 def get_es_query(es, terms, order, classifiers):
  52     """
  53     Returns an Elasticsearch query from data from the request.
  54     """
  55     if not terms:
  56         query = es.query()
  57     else:
  58         bool_query = gather_es_queries(terms)
  59         query = es.query(bool_query)
  60         query = query.suggest("name_suggestion", terms, term={"field": "name"})
  61 
  62     # Require match to all specified classifiers
  63     for classifier in classifiers:
  64         query = query.query("prefix", classifiers=classifier)
  65 
  66     query = query_for_order(query, order)
  67     return query
  68 
  69 
  70 def gather_es_queries(q):
  71     quoted_string, unquoted_string = filter_query(q)
  72     must = [form_query("phrase", i) for i in quoted_string] + [
  73         form_query("best_fields", i) for i in unquoted_string
  74     ]
  75 
  76     bool_query = Q("bool", must=must)
  77 
  78     # Allow to optionally match on prefix
  79     # if ``q`` is longer than one character.
  80     if len(q) > 1:
  81         bool_query = bool_query | Q("prefix", normalized_name=q)
  82     return bool_query
  83 
  84 
  85 def filter_query(s):
  86     """
  87     Filters given query with the below regex
  88     and returns lists of quoted and unquoted strings
  89     """
  90     matches = re.findall(r'(?:"([^"]*)")|([^"]*)', s)
  91     result_quoted = [t[0].strip() for t in matches if t[0]]
  92     result_unquoted = [t[1].strip() for t in matches if t[1]]
  93     return result_quoted, result_unquoted
  94 
  95 
  96 def form_query(query_type, query):
  97     """
  98     Returns a multi match query
  99     """
  100     fields = [
  101         field + "^" + str(SEARCH_BOOSTS[field]) if field in SEARCH_BOOSTS else field
  102         for field in SEARCH_FIELDS
  103     ]
  104     return Q("multi_match", fields=fields, query=query, type=query_type)
  105 
  106 
  107 def query_for_order(query, order):
  108     """
  109     Applies transformations on the ES query based on the search order.
  110 
  111     Order is assumed to be a string with the name of a field with an optional
  112     hyphen to indicate descending sort order.
  113     """
  114     if order == "":  # relevance should not sort
  115         return query
  116 
  117     field = order[order.find("-") + 1 :]
  118     sort_info = {
  119         field: {
  120             "order": "desc" if order.startswith("-") else "asc",
  121             "unmapped_type": "long",
  122         }
  123     }
  124     query = query.sort(sort_info)
  125     return query
  126
  ```
  --- 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/warehouse/search/queries.py b/warehouse/search/queries.py
  --- a/warehouse/search/queries.py
  +++ b/warehouse/search/queries.py
  @@ -52,36 +52,45 @@
       """
       Returns an Elasticsearch query from data from the request.
       """
  +    classifier_q = Q(
  +        "bool",
  +        # Theh results must have all selected classifiers
  +        must=[
  +            Q(
  +                "bool",
  +                should=[
  +                    # Term search for the exact classifier
  +                    Q("term", classifiers=classifier),
  +                    # Prefix search for potential children classifiers
  +                    Q("prefix", classifiers=classifier + " :: "),
  +                ],
  +            )
  +            for classifier in classifiers
  +        ],
  +    )
       if not terms:
  -        query = es.query()
  +        query = es.query(classifier_q) if classifiers else es.query()
       else:
  -        bool_query = gather_es_queries(terms)
  +        quoted_string, unquoted_string = filter_query(terms)
  +        bool_query = Q(
  +            "bool",
  +            must=[form_query("phrase", i) for i in quoted_string]
  +            + [form_query("best_fields", i) for i in unquoted_string]
  +            + ([classifier_q] if classifiers else []),
  +        )
  +
  +        # Allow to optionally match on prefix
  +        # if ``q`` is longer than one character.
  +        if len(terms) > 1:
  +            bool_query = bool_query | Q("prefix", normalized_name=terms)
  +
           query = es.query(bool_query)
           query = query.suggest("name_suggestion", terms, term={"field": "name"})
   
  -    # Require match to all specified classifiers
  -    for classifier in classifiers:
  -        query = query.query("prefix", classifiers=classifier)
  -
       query = query_for_order(query, order)
       return query
   
   
  -def gather_es_queries(q):
  -    quoted_string, unquoted_string = filter_query(q)
  -    must = [form_query("phrase", i) for i in quoted_string] + [
  -        form_query("best_fields", i) for i in unquoted_string
  -    ]
  -
  -    bool_query = Q("bool", must=must)
  -
  -    # Allow to optionally match on prefix
  -    # if ``q`` is longer than one character.
  -    if len(q) > 1:
  -        bool_query = bool_query | Q("prefix", normalized_name=q)
  -    return bool_query
  -
  -
   def filter_query(s):
       """
       Filters given query with the below regex
  
verification_info: {"golden_diff": "diff --git a/warehouse/search/queries.py b/warehouse/search/queries.py\n--- a/warehouse/search/queries.py\n+++ b/warehouse/search/queries.py\n@@ -52,36 +52,45 @@\n     \"\"\"\n     Returns an Elasticsearch query from data from the request.\n     \"\"\"\n+    classifier_q = Q(\n+        \"bool\",\n+        # Theh results must have all selected classifiers\n+        must=[\n+            Q(\n+                \"bool\",\n+                should=[\n+                    # Term search for the exact classifier\n+                    Q(\"term\", classifiers=classifier),\n+                    # Prefix search for potential children classifiers\n+                    Q(\"prefix\", classifiers=classifier + \" :: \"),\n+                ],\n+            )\n+            for classifier in classifiers\n+        ],\n+    )\n     if not terms:\n-        query = es.query()\n+        query = es.query(classifier_q) if classifiers else es.query()\n     else:\n-        bool_query = gather_es_queries(terms)\n+        quoted_string, unquoted_string = filter_query(terms)\n+        bool_query = Q(\n+            \"bool\",\n+            must=[form_query(\"phrase\", i) for i in quoted_string]\n+            + [form_query(\"best_fields\", i) for i in unquoted_string]\n+            + ([classifier_q] if classifiers else []),\n+        )\n+\n+        # Allow to optionally match on prefix\n+        # if ``q`` is longer than one character.\n+        if len(terms) > 1:\n+            bool_query = bool_query | Q(\"prefix\", normalized_name=terms)\n+\n         query = es.query(bool_query)\n         query = query.suggest(\"name_suggestion\", terms, term={\"field\": \"name\"})\n \n-    # Require match to all specified classifiers\n-    for classifier in classifiers:\n-        query = query.query(\"prefix\", classifiers=classifier)\n-\n     query = query_for_order(query, order)\n     return query\n \n \n-def gather_es_queries(q):\n-    quoted_string, unquoted_string = filter_query(q)\n-    must = [form_query(\"phrase\", i) for i in quoted_string] + [\n-        form_query(\"best_fields\", i) for i in unquoted_string\n-    ]\n-\n-    bool_query = Q(\"bool\", must=must)\n-\n-    # Allow to optionally match on prefix\n-    # if ``q`` is longer than one character.\n-    if len(q) > 1:\n-        bool_query = bool_query | Q(\"prefix\", normalized_name=q)\n-    return bool_query\n-\n-\n def filter_query(s):\n     \"\"\"\n     Filters given query with the below regex\n", "issue": "Python 3.1 classifier filtering is broken\nWhen [filtering by the `Programming Language :: Python :: 3.1` classifier on pypi.org][1], the results include packages which don't have that classifier - any package with a classifier matching `Programming Language :: Python :: 3.1*` is included. That is, packages for 3.10, 3.11, 3.12, and so on are included in the results.\r\n\r\n[1]: https://pypi.org/search/?q=&o=&c=Programming+Language+%3A%3A+Python+%3A%3A+3.1\r\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nfrom elasticsearch_dsl import Q\n\nSEARCH_FIELDS = [\n    \"author\",\n    \"author_email\",\n    \"description\",\n    \"download_url\",\n    \"home_page\",\n    \"keywords\",\n    \"license\",\n    \"maintainer\",\n    \"maintainer_email\",\n    \"normalized_name\",\n    \"platform\",\n    \"summary\",\n]\nSEARCH_BOOSTS = {\n    \"name\": 10,\n    \"normalized_name\": 10,\n    \"description\": 5,\n    \"keywords\": 5,\n    \"summary\": 5,\n}\nSEARCH_FILTER_ORDER = (\n    \"Framework\",\n    \"Topic\",\n    \"Development Status\",\n    \"License\",\n    \"Programming Language\",\n    \"Operating System\",\n    \"Environment\",\n    \"Intended Audience\",\n    \"Natural Language\",\n)\n\n\ndef get_es_query(es, terms, order, classifiers):\n    \"\"\"\n    Returns an Elasticsearch query from data from the request.\n    \"\"\"\n    if not terms:\n        query = es.query()\n    else:\n        bool_query = gather_es_queries(terms)\n        query = es.query(bool_query)\n        query = query.suggest(\"name_suggestion\", terms, term={\"field\": \"name\"})\n\n    # Require match to all specified classifiers\n    for classifier in classifiers:\n        query = query.query(\"prefix\", classifiers=classifier)\n\n    query = query_for_order(query, order)\n    return query\n\n\ndef gather_es_queries(q):\n    quoted_string, unquoted_string = filter_query(q)\n    must = [form_query(\"phrase\", i) for i in quoted_string] + [\n        form_query(\"best_fields\", i) for i in unquoted_string\n    ]\n\n    bool_query = Q(\"bool\", must=must)\n\n    # Allow to optionally match on prefix\n    # if ``q`` is longer than one character.\n    if len(q) > 1:\n        bool_query = bool_query | Q(\"prefix\", normalized_name=q)\n    return bool_query\n\n\ndef filter_query(s):\n    \"\"\"\n    Filters given query with the below regex\n    and returns lists of quoted and unquoted strings\n    \"\"\"\n    matches = re.findall(r'(?:\"([^\"]*)\")|([^\"]*)', s)\n    result_quoted = [t[0].strip() for t in matches if t[0]]\n    result_unquoted = [t[1].strip() for t in matches if t[1]]\n    return result_quoted, result_unquoted\n\n\ndef form_query(query_type, query):\n    \"\"\"\n    Returns a multi match query\n    \"\"\"\n    fields = [\n        field + \"^\" + str(SEARCH_BOOSTS[field]) if field in SEARCH_BOOSTS else field\n        for field in SEARCH_FIELDS\n    ]\n    return Q(\"multi_match\", fields=fields, query=query, type=query_type)\n\n\ndef query_for_order(query, order):\n    \"\"\"\n    Applies transformations on the ES query based on the search order.\n\n    Order is assumed to be a string with the name of a field with an optional\n    hyphen to indicate descending sort order.\n    \"\"\"\n    if order == \"\":  # relevance should not sort\n        return query\n\n    field = order[order.find(\"-\") + 1 :]\n    sort_info = {\n        field: {\n            \"order\": \"desc\" if order.startswith(\"-\") else \"asc\",\n            \"unmapped_type\": \"long\",\n        }\n    }\n    query = query.sort(sort_info)\n    return query\n", "path": "warehouse/search/queries.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nfrom elasticsearch_dsl import Q\n\nSEARCH_FIELDS = [\n    \"author\",\n    \"author_email\",\n    \"description\",\n    \"download_url\",\n    \"home_page\",\n    \"keywords\",\n    \"license\",\n    \"maintainer\",\n    \"maintainer_email\",\n    \"normalized_name\",\n    \"platform\",\n    \"summary\",\n]\nSEARCH_BOOSTS = {\n    \"name\": 10,\n    \"normalized_name\": 10,\n    \"description\": 5,\n    \"keywords\": 5,\n    \"summary\": 5,\n}\nSEARCH_FILTER_ORDER = (\n    \"Framework\",\n    \"Topic\",\n    \"Development Status\",\n    \"License\",\n    \"Programming Language\",\n    \"Operating System\",\n    \"Environment\",\n    \"Intended Audience\",\n    \"Natural Language\",\n)\n\n\ndef get_es_query(es, terms, order, classifiers):\n    \"\"\"\n    Returns an Elasticsearch query from data from the request.\n    \"\"\"\n    classifier_q = Q(\n        \"bool\",\n        # Theh results must have all selected classifiers\n        must=[\n            Q(\n                \"bool\",\n                should=[\n                    # Term search for the exact classifier\n                    Q(\"term\", classifiers=classifier),\n                    # Prefix search for potential children classifiers\n                    Q(\"prefix\", classifiers=classifier + \" :: \"),\n                ],\n            )\n            for classifier in classifiers\n        ],\n    )\n    if not terms:\n        query = es.query(classifier_q) if classifiers else es.query()\n    else:\n        quoted_string, unquoted_string = filter_query(terms)\n        bool_query = Q(\n            \"bool\",\n            must=[form_query(\"phrase\", i) for i in quoted_string]\n            + [form_query(\"best_fields\", i) for i in unquoted_string]\n            + ([classifier_q] if classifiers else []),\n        )\n\n        # Allow to optionally match on prefix\n        # if ``q`` is longer than one character.\n        if len(terms) > 1:\n            bool_query = bool_query | Q(\"prefix\", normalized_name=terms)\n\n        query = es.query(bool_query)\n        query = query.suggest(\"name_suggestion\", terms, term={\"field\": \"name\"})\n\n    query = query_for_order(query, order)\n    return query\n\n\ndef filter_query(s):\n    \"\"\"\n    Filters given query with the below regex\n    and returns lists of quoted and unquoted strings\n    \"\"\"\n    matches = re.findall(r'(?:\"([^\"]*)\")|([^\"]*)', s)\n    result_quoted = [t[0].strip() for t in matches if t[0]]\n    result_unquoted = [t[1].strip() for t in matches if t[1]]\n    return result_quoted, result_unquoted\n\n\ndef form_query(query_type, query):\n    \"\"\"\n    Returns a multi match query\n    \"\"\"\n    fields = [\n        field + \"^\" + str(SEARCH_BOOSTS[field]) if field in SEARCH_BOOSTS else field\n        for field in SEARCH_FIELDS\n    ]\n    return Q(\"multi_match\", fields=fields, query=query, type=query_type)\n\n\ndef query_for_order(query, order):\n    \"\"\"\n    Applies transformations on the ES query based on the search order.\n\n    Order is assumed to be a string with the name of a field with an optional\n    hyphen to indicate descending sort order.\n    \"\"\"\n    if order == \"\":  # relevance should not sort\n        return query\n\n    field = order[order.find(\"-\") + 1 :]\n    sort_info = {\n        field: {\n            \"order\": \"desc\" if order.startswith(\"-\") else \"asc\",\n            \"unmapped_type\": \"long\",\n        }\n    }\n    query = query.sort(sort_info)\n    return query\n", "path": "warehouse/search/queries.py"}]}