Repository: LMFDB/lmfdb

Issue:
Cyclic isogeny degree search results
The search 
  http://www.lmfdb.org/EllipticCurve/Q/?count=None&hst=List&isogeny_degrees=200-300&search_type=List
for elliptic curves with a cyclic isogeny of degree 200-300 returns all elliptic curves?!  Actually, I get the same issue with input "5-6", but not with "37", so it must be interpreting ranged input incorrectly.

Also (whoever gets there), the knowl title "Bad $p$" also has a huge space in it between "Bad" and "$p$".


Discussion:

Resolving patch:
diff --git a/lmfdb/elliptic_curves/elliptic_curve.py b/lmfdb/elliptic_curves/elliptic_curve.py
--- a/lmfdb/elliptic_curves/elliptic_curve.py
+++ b/lmfdb/elliptic_curves/elliptic_curve.py
@@ -405,7 +405,7 @@ def elliptic_curve_search(info, query):
             query['cm'] = {'$ne' : 0}
         else:
             parse_ints(info,query,field='cm',qfield='cm')
-    parse_element_of(info,query,'isogeny_degrees',split_interval=1000,contained_in=get_stats().isogeny_degrees)
+    parse_element_of(info,query,'isogeny_degrees',split_interval=200,contained_in=get_stats().isogeny_degrees)
     parse_primes(info, query, 'nonmax_primes', name='non-maximal primes',
                  qfield='nonmax_primes', mode=info.get('nonmax_quantifier'), radical='nonmax_rad')
     parse_primes(info, query, 'bad_primes', name='bad primes',
@@ -893,7 +893,7 @@ def __init__(self):
         bad_primes = TextBoxWithSelect(
             name="bad_primes",
             label="Bad primes $p$",
-            short_label="Bad $p$",
+            short_label=r"Bad$\ p$",
             knowl="ec.q.reduction_type",
             example="5,13",
             select_box=bad_quant)
@@ -902,7 +902,7 @@ def __init__(self):
         sha_primes = TextBoxWithSelect(
             name="sha_primes",
             label="$p$ dividing |&#1064;|",
-            short_label="$p$ div |&#1064;|",
+            short_label="$p\ $div$\ $|&#1064;|",
             knowl="ec.analytic_sha_order",
             example="3,5",
             select_box=sha_quant)
@@ -938,7 +938,7 @@ def __init__(self):
         nonmax_primes = TextBoxWithSelect(
             name="nonmax_primes",
             label=r"Nonmaximal $\ell$",
-            short_label=r"Nonmax $\ell$",
+            short_label=r"Nonmax$\ \ell$",
             knowl="ec.maximal_elladic_galois_rep",
             example="2,3",
             select_box=nonmax_quant)
diff --git a/lmfdb/utils/search_parsing.py b/lmfdb/utils/search_parsing.py
--- a/lmfdb/utils/search_parsing.py
+++ b/lmfdb/utils/search_parsing.py
@@ -4,7 +4,6 @@
 import re
 import sys
 from collections import Counter
-
 from lmfdb.utils.utilities import flash_error
 from sage.all import ZZ, QQ, prod, PolynomialRing, pari
 from sage.misc.decorators import decorator_keywords
@@ -14,6 +13,7 @@
 from lmfdb.backend.utils import SearchParsingError
 from .utilities import coeff_to_poly
 from math import log2
+import ast
 
 SPACES_RE = re.compile(r"\d\s+\d")
 LIST_RE = re.compile(r"^(\d+|(\d*-(\d+)?))(,(\d+|(\d*-(\d+)?)))*$")
@@ -36,16 +36,11 @@
 # PAIR_RE = re.compile(r'^\[\d+,\d+\]$')
 # IF_RE = re.compile(r'^\[\]|(\[\d+(,\d+)*\])$')  # invariant factors
 FLOAT_RE = re.compile("^" + FLOAT_STR + "$")
-BRACKETING_RE = re.compile(
-    r"(\[[^\]]*\])"
-)  # won't work for iterated brackets [[a,b],[c,d]]
+BRACKETING_RE = re.compile(r"(\[[^\]]*\])")  # won't work for iterated brackets [[a,b],[c,d]]
 PREC_RE = re.compile(r"^-?((?:\d+(?:[.]\d*)?)|(?:[.]\d+))(?:e([-+]?\d+))?$")
 LF_LABEL_RE = re.compile(r"^\d+\.\d+\.\d+\.\d+$")
 MULTISET_RE = re.compile(r"^(\d+)(\^(\d+))?(,(\d+)(\^(\d+))?)*$")
 
-import ast
-
-
 class PowMulNodeVisitor(ast.NodeTransformer):
     def visit_BinOp(self, node):
         if isinstance(node.op, ast.Pow):
@@ -63,7 +58,6 @@ def visit_Constant(self, node):
         def visit_Num(self, node):  # deprecated for python >= 3.8
             return self.visit_Constant(node)
 
-
 class SearchParser(object):
     def __init__(
         self,
@@ -103,9 +97,7 @@ def __call__(self, info, query, field=None, name=None, qfield=None, *args, **kwd
                     name = self.default_name
             inp = str(inp)
             if SPACES_RE.search(inp):
-                raise SearchParsingError(
-                    "You have entered spaces in between digits. Please add a comma or delete the spaces."
-                )
+                raise SearchParsingError("You have entered spaces in between digits. Please add a comma or delete the spaces.")
             inp = clean_input(inp, self.clean_spaces)
             if qfield is None:
                 if field is None:
@@ -135,24 +127,12 @@ def __call__(self, info, query, field=None, name=None, qfield=None, *args, **kwd
             return rval
         except (ValueError, AttributeError, TypeError) as err:
             if self.error_is_safe:
-                flash_error(
-                    "<span style='color:black'>%s</span> is not a valid input for <span style='color:black'>%s</span>. "
-                    + str(err)
-                    + ".",
-                    inp,
-                    name,
-                )
+                flash_error(f"<span style='color:black'>%s</span> is not a valid input for <span style='color:black'>%s</span>. {err}.", inp, name)
             else:
-                flash_error(
-                    "<span style='color:black'>%s</span> is not a valid input for <span style='color:black'>%s</span>. %s",
-                    inp,
-                    name,
-                    str(err),
-                )
+                flash_error("<span style='color:black'>%s</span> is not a valid input for <span style='color:black'>%s</span>. %s", inp, name, str(err))
             info["err"] = ""
             raise
 
-
 @decorator_keywords
 def search_parser(
     f,
@@ -179,7 +159,6 @@ def search_parser(
         clean_spaces,
     )
 
-
 # Remove whitespace for simpler parsing
 # Remove brackets to avoid tricks (so we can echo it back safely)
 def clean_input(inp, clean_spaces=True):
@@ -190,13 +169,11 @@ def clean_input(inp, clean_spaces=True):
     else:
         return re.sub(r"[<>]", "", str(inp))
 
-
 def prep_ranges(inp):
     if inp is None:
         return None
     return inp.replace("..", "-").replace(" ", "")
 
-
 def prep_raw(inp, names={}):
     """
     Prepare an input string for being passed as a ``$raw`` value to the database search.
@@ -212,9 +189,9 @@ def prep_raw(inp, names={}):
 
     This function will raise a SearchParsingError if there is a syntax error or if there is a variable that's not in the names list
     """
-    inp = implicit_mul(
-        inp, level=10
-    )  # level = 10 includes (a+b)(c+d) -> (a+b)*(c+d) which isn't safe in Sage but should be okay for us
+    # level = 10 includes (a+b)(c+d) -> (a+b)*(c+d) which isn't safe in Sage
+    #but should be okay for us
+    inp = implicit_mul(inp, level=10)
 
     def filtered_var(s):
         if s not in names:
@@ -236,7 +213,6 @@ def filtered_var(s):
             processed.append(piece)
     return {"$raw": "".join(processed)}
 
-
 # Various modules need to split a list of integers more simply
 def split_list(s):
     s = s.replace(" ", "")[1:-1]
@@ -244,7 +220,6 @@ def split_list(s):
         return [int(a) for a in s.split(",")]
     return []
 
-
 # This function can be used by modules to get a list of ints
 # or an iterator (Python3 range) that matches the results of parse_ints below
 # useful when a module wants to iterate over key values being
@@ -284,19 +259,13 @@ def parse_ints_to_list(arg, max_val=None):
         return [k for k in range(int(m), int(M) + 1)]
     return [int(s)]
 
-
 def parse_ints_to_list_flash(arg, name, max_val=None):
     try:
         return parse_ints_to_list(arg, max_val)
     except ValueError:
-        flash_error(
-            "Error: <span style='color:black'>%s</span> is not a valid input for <span style='color:black'>%s</span>. It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).",
-            arg,
-            name,
-        )
+        flash_error("Error: <span style='color:black'>%s</span> is not a valid input for <span style='color:black'>%s</span>. It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).", arg, name)
         raise
 
-
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_list(inp, query, qfield, process=None):
     """
@@ -311,16 +280,13 @@ def parse_list(inp, query, qfield, process=None):
     else:
         query[qfield] = out
 
-
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_multiset(inp, query, qfield):
     """
     Parses a string representing a multiset of integers, written in the form, e.g., '1^1,2^4,4^1', and converts it to a list of pairs, e.g., [[1,1], [2,4], [4,1]]
     """
     if not MULTISET_RE.match(inp):
-        raise SearchParsingError(
-            "Multisets must be given as comma-separated strings with entries of the form <element>^<multiplicity>."
-        )
+        raise SearchParsingError("Multisets must be given as comma-separated strings with entries of the form <element>^<multiplicity>.")
     spl = inp.split(",")
     o_list = []
     for s in spl:
@@ -332,7 +298,6 @@ def parse_multiset(inp, query, qfield):
     o_list.sort()
     query[qfield] = o_list
 
-
 def parse_range(arg, parse_singleton=int, use_dollar_vars=True):
     # TODO: graceful errors
     if type(arg) == parse_singleton:
@@ -354,7 +319,6 @@ def parse_range(arg, parse_singleton=int, use_dollar_vars=True):
     else:
         return parse_singleton(arg)
 
-
 # version above does not produce legal results when there is a comma
 # to deal with $or, we return [key, value]
 def parse_range2(arg, key, parse_singleton=int, parse_endpoint=None, split_minus=True):
@@ -388,7 +352,6 @@ def parse_range2(arg, key, parse_singleton=int, parse_endpoint=None, split_minus
     else:
         return [key, parse_singleton(arg)]
 
-
 # Like parse_range2, but to deal with strings which could be rational numbers
 # process is a function to apply to arguments after they have been parsed
 def parse_range2rat(arg, key, process):
@@ -412,7 +375,6 @@ def parse_range2rat(arg, key, process):
     else:
         return [key, process(arg)]
 
-
 # We parse into a list of singletons and pairs, like [[-5,-2], 10, 11, [16,100]]
 # If split0, we split ranges [-a,b] that cross 0 into [-a, -1], [1, b]
 def parse_range3(arg, split0=False):
@@ -426,15 +388,11 @@ def parse_range3(arg, split0=False):
         if start:
             low = ZZ(str(start))
         else:
-            raise SearchParsingError(
-                "It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121)."
-            )
+            raise SearchParsingError("It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).")
         if end:
             high = ZZ(str(end))
         else:
-            raise SearchParsingError(
-                "It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121)."
-            )
+            raise SearchParsingError("It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).")
         if low == high:
             return [low]
         if split0 and low < 0 and high > 0:
@@ -452,7 +410,6 @@ def parse_range3(arg, split0=False):
     else:
         return [ZZ(str(arg))]
 
-
 def integer_options(arg, max_opts=None, contained_in=None):
     if not LIST_RE.match(arg) and MULT_PARSE.fullmatch(arg):
         # Make input work using some arithmetic expressions
@@ -487,7 +444,6 @@ def integer_options(arg, max_opts=None, contained_in=None):
             raise ValueError("Too many options")
     return sorted(list(ans))
 
-
 def collapse_ors(parsed, query):
     # work around syntax for $or
     # we have to foil out multiple or conditions
@@ -504,10 +460,8 @@ def collapse_ors(parsed, query):
         return
     query[parsed[0]] = parsed[1]
 
-
-@search_parser(
-    clean_info=True, prep_plus=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_plus=True)
 def parse_rational(inp, query, qfield):
     if QQ_RE.match(inp):
         query[qfield] = str(QQ(inp))
@@ -515,32 +469,22 @@ def parse_rational(inp, query, qfield):
         opts = [{qfield: str(QQ(x))} for x in inp.split(",")]
         collapse_ors(["$or", opts], query)
     else:
-        raise SearchParsingError(
-            "It needs to be a rational number or comma separated list of rationals."
-        )
+        raise SearchParsingError("It needs to be a rational number or comma separated list of rationals.")
 
-
-@search_parser(
-    clean_info=True, prep_plus=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_plus=True)
 def parse_rational_to_list(inp, query, qfield):
     if QQ_RE.match(inp):
         qinp = QQ(inp)
         query[qfield] = [qinp.numerator(), qinp.denominator()]
     elif QQ_LIST_RE.match(inp):
-        opts = [
-            {qfield: [QQ(x).numerator(), QQ(x).denominator()]} for x in inp.split(",")
-        ]
+        opts = [{qfield: [QQ(x).numerator(), QQ(x).denominator()]} for x in inp.split(",")]
         collapse_ors(["$or", opts], query)
     else:
-        raise SearchParsingError(
-            "It needs to be a rational number or comma separated list of rationals."
-        )
+        raise SearchParsingError("It needs to be a rational number or comma separated list of rationals.")
 
-
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_ints(inp, query, qfield, parse_singleton=int):
     if LIST_RE.match(inp):
         collapse_ors(parse_range2(inp, qfield, parse_singleton), query)
@@ -552,40 +496,27 @@ def parse_ints(inp, query, qfield, parse_singleton=int):
         except (TypeError, ValueError, SyntaxError):
             raise SearchParsingError("Unable to evaluate expression.")
     else:
-        raise SearchParsingError(
-            "It needs to be an integer (such as 25), be a multiplicative expression that parses to an integer (such as 2^2*3), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121)."
-        )
+        raise SearchParsingError("It needs to be an integer (such as 25), be a multiplicative expression that parses to an integer (such as 2^2*3), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).")
 
 
-@search_parser(
-    clean_info=True, clean_spaces=False, prep_ranges=False
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, clean_spaces=False, prep_ranges=False)
 def parse_ints_raw(inp, query, qfield, names={}):
     # This version of parse_ints allows the user to use arithmetic expressions involving database columns
     # We let postgres do most of the parsing and don't raise an error here on any input (since it's tricky to determine what's valid)
     if re.search(r"[A-Za-z]", inp):
-        collapse_ors(
-            parse_range2(
-                inp, qfield, lambda inp: prep_raw(inp, names), split_minus=False
-            ),
-            query,
-        )
+        collapse_ors(parse_range2(inp, qfield, lambda inp: prep_raw(inp, names), split_minus=False), query)
     else:
         # If there are no letters we allow - to indicate a range.
         collapse_ors(parse_range2(inp, qfield, int), query)
 
-
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_posints(inp, query, qfield, parse_singleton=int):
     if LIST_POSINT_RE.match(inp):
         collapse_ors(parse_range2(inp, qfield, parse_singleton), query)
     else:
-        raise SearchParsingError(
-            "It needs to be a positive integer (such as 25), a range of positive integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121)."
-        )
-
+        raise SearchParsingError("It needs to be a positive integer (such as 25), a range of positive integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).")
 
 def parse_range_float(arg, key, exact_den=4, exact_prec=10, mod1=False):
     # Trying to adapt parse_range2 was causing too many headaches
@@ -700,11 +631,9 @@ def is_exact(x):
             s, e = sred, ered
             if s > e:
                 # Interval crosses 0.5, so we have to split it in two
-                return (
-                    ["$or", [{key: {"$gte": s - eps()}}, {key: {"$lte": e + eps()}}]],
-                    "%s-0.5,-0.5-%s" % (start, end),
-                )
-        return ([key, {"$gte": s - eps(), "$lte": e + eps()}], "%s-%s" % (start, end))
+                return (["$or", [{key: {"$gte": s - eps()}}, {key: {"$lte": e + eps()}}]],
+                        f"{start}-0.5,-0.5-{end}")
+        return ([key, {"$gte": s - eps(), "$lte": e + eps()}], f"{start}-{end}")
     else:
         x = my_float(arg)
         if mod1:
@@ -717,15 +646,10 @@ def is_exact(x):
             prec = exact_prec
         start = show_float(x - eps(prec), prec=prec + 1)
         end = show_float(x + eps(prec), prec=prec + 1)
-        return [key, {"$gte": x - eps(prec), "$lte": x + eps(prec)}], "%s-%s" % (
-            start,
-            end,
-        )
-
+        return [key, {"$gte": x - eps(prec), "$lte": x + eps(prec)}], f"{start}-{end}"
 
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_floats(inp, query, qfield):
     if LIST_FLOAT_RE.match(inp):
         A, clauses = parse_range_float(inp, qfield)
@@ -735,10 +659,8 @@ def parse_floats(inp, query, qfield):
         msg = "It needs to be an float (such as 25 or 25.0), a range of floats (such as 2.1-8.7), or a comma-separated list of these (such as 4,9.2,16 or 4-25.1, 81-121)."
         raise SearchParsingError(msg)
 
-
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_mod1(inp, query, qfield, exact_den=4, exact_prec=10):
     if LIST_FLOAT_RE.match(inp):
         A, clauses = parse_range_float(inp, qfield, mod1=True)
@@ -748,39 +670,31 @@ def parse_mod1(inp, query, qfield, exact_den=4, exact_prec=10):
         msg = "It needs to be an float (such as 0.25), a rational (such as 1/4), a range of floats (such as 0.2-0.7), or a comma-separated list of these (such as -0.25,0,0.25-0.5)."
         raise SearchParsingError(msg)
 
-
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
-def parse_element_of(
-    inp, query, qfield, split_interval=False, parse_singleton=int, contained_in=None
-):
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
+def parse_element_of(inp, query, qfield, split_interval=False, parse_singleton=int, contained_in=None):
     if split_interval:
-        options = integer_options(
-            inp, max_opts=split_interval, contained_in=contained_in
-        )
+        options = integer_options(inp, max_opts=split_interval, contained_in=contained_in)
         if len(options) == 1:
             query[qfield] = {"$contains": options}
         elif len(options) > 1:
             query[qfield] = {"$overlaps": options}
-            # query[qfield] = {'$or': [{'$contains': [n]} for n in options]}
+        else:
+            # element of the empty set should return no results (this can easily happen if contained_in is specified, or for empty intervals like [2,1])
+            query[qfield] = {"$and": [{"$exists": True}, {"$exists": False}]}
     else:
         query[qfield] = {"$contains": [parse_singleton(inp)]}
 
-
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_not_element_of(inp, query, qfield, parse_singleton=int):
     if qfield not in query:
         query[qfield] = {}
     query[qfield]["$notcontains"] = [parse_singleton(inp)]
 
-
 # Parses signed ints as an int and a sign the fields these are stored are passed in as qfield = (sign_field, abs_field)
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_signed_ints(inp, query, qfield, parse_one=None):
     if parse_one is None:
         parse_one = lambda x: (int(x.sign()), int(x.abs())) if x != 0 else (1, 0)
@@ -823,24 +737,17 @@ def parse_signed_ints(inp, query, qfield, parse_one=None):
                     iquery.append({sign_field: s0, abs_field: abs_D})
             collapse_ors(["$or", iquery], query)
     else:
-        raise SearchParsingError(
-            "It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121)."
-        )
+        raise SearchParsingError("It needs to be an integer (such as 25), a range of integers (such as 2-10 or 2..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).")
 
-
-@search_parser(
-    clean_info=True, prep_ranges=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, prep_ranges=True)
 def parse_rats(inp, query, qfield, process=None):
     if process is None:
         process = lambda x: x
     if LIST_RAT_RE.match(inp):
         collapse_ors(parse_range2rat(inp, qfield, process), query)
     else:
-        raise SearchParsingError(
-            "It needs to be a non-negative rational number (such as 4/3), a range of non-negative rational numbers (such as 2-5/2 or 2.5..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121)."
-        )
-
+        raise SearchParsingError("It needs to be a non-negative rational number (such as 4/3), a range of non-negative rational numbers (such as 2-5/2 or 2.5..10), or a comma-separated list of these (such as 4,9,16 or 4-25, 81-121).")
 
 def _parse_subset(inp, query, qfield, mode, radical, product):
     def add_condition(kwd):
@@ -877,18 +784,13 @@ def add_condition(kwd):
         else:
             dup_free = []
         if qfield in query:
-            raise SearchParsingError(
-                "Cannot specify containment and equality simultaneously"
-            )
+            raise SearchParsingError("Cannot specify containment and equality simultaneously")
         query[qfield] = dup_free
     else:
         raise ValueError("Unrecognized mode: programming error in LMFDB code")
 
-
 @search_parser
-def parse_subset(
-    inp, query, qfield, parse_singleton=None, mode=None, radical=None, product=prod
-):
+def parse_subset(inp, query, qfield, parse_singleton=None, mode=None, radical=None, product=prod):
     # Note that you can do sanity checking using parse_singleton
     # Just raise a ValueError if it fails.
     inp = inp.split(",")
@@ -896,14 +798,12 @@ def parse_subset(
         inp = [parse_singleton(x) for x in inp]
     _parse_subset(inp, query, qfield, mode, radical, product)
 
-
 def _multiset_code(n):
     # We encode multiplicities by appending consecutive letters: A, B,..., BA, BB, BC,...
     if n == 0:
         return "A"
     return "".join(chr(65 + d) for d in reversed(ZZ(n).digits(26)))
 
-
 def _multiset_encode(L):
     # L should be a list of strings
     distinguished = []
@@ -913,7 +813,6 @@ def _multiset_encode(L):
         seen[x] += 1
     return distinguished
 
-
 @search_parser(clean_info=True)
 def parse_submultiset(inp, query, qfield, mode=None):
     # Only multisets of strings are supported.
@@ -929,25 +828,19 @@ def parse_submultiset(inp, query, qfield, mode=None):
         # radical doesn't make sense (you should use subset instead of multiset)
         _parse_subset(_multiset_encode(inp.split(",")), query, qfield, mode, None, None)
 
-
-@search_parser(
-    clean_info=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True)
 def parse_primes(inp, query, qfield, mode=None, radical=None):
     format_ok = LIST_POSINT_RE.match(inp)
     if format_ok:
         primes = [int(p) for p in inp.split(",")]
         format_ok = all([ZZ(p).is_prime(proof=False) for p in primes])
     if not format_ok:
-        raise SearchParsingError(
-            "It needs to be a prime (such as 5), or a comma-separated list of primes (such as 2,3,11)."
-        )
+        raise SearchParsingError("It needs to be a prime (such as 5), or a comma-separated list of primes (such as 2,3,11).")
     _parse_subset(primes, query, qfield, mode, radical, prod)
 
-
-@search_parser(
-    clean_info=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True)
 def parse_bracketed_posints(
     inp,
     query,
@@ -992,13 +885,9 @@ def parse_bracketed_posints(
         else:
             lstr = "list of positive integers"
             example = "[1,2,3] or [5,6]"
-        raise SearchParsingError(
-            "It needs to be a %s in square brackets, such as %s." % (lstr, example)
-        )
+        raise SearchParsingError("It needs to be a %s in square brackets, such as %s." % (lstr, example))
     else:
-        if (
-            inp == "[]"
-        ):  # fixes bug in the code below (split never returns an empty list)
+        if (inp == "[]"):  # fixes bug in the code below (split never returns an empty list)
             if split:
                 query[qfield] = []
             else:
@@ -1010,17 +899,13 @@ def parse_bracketed_posints(
             # L = [int(a) for a in inp[1:-1].split(',')]
             for i in range(len(L) - 1):
                 if L[i] % L[i + 1] != 0:
-                    raise SearchParsingError(
-                        "Each entry must divide the previous, such as [4,2]."
-                    )
+                    raise SearchParsingError("Each entry must divide the previous, such as [4,2].")
         elif check_divisibility == "increasing":
             # Check that each entry divides the previous
             # L = [int(a) for a in inp[1:-1].split(',')]
             for i in range(len(L) - 1):
                 if L[i + 1] % L[i] != 0:
-                    raise SearchParsingError(
-                        "Each entry must divide the next, such as [2,4]."
-                    )
+                    raise SearchParsingError("Each entry must divide the next, such as [2,4].")
         if process is not None:
             L = [process(a) for a in L]
         if listprocess is not None:
@@ -1054,10 +939,8 @@ def parse_bracketed_posints(
             inp = "[%s]" % ",".join(str(a) for a in L)
             query[qfield] = inp if keepbrackets else inp[1:-1]
 
-
-@search_parser(
-    clean_info=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True)
 def parse_bracketed_rats(
     inp,
     query,
@@ -1115,13 +998,9 @@ def parse_bracketed_rats(
         else:
             lstr = "list of rational numbers"
             example = "[1/7,2,3] or [5,6/71]"
-        raise SearchParsingError(
-            "It needs to be a %s in square brackets, such as %s." % (lstr, example)
-        )
+        raise SearchParsingError("It needs to be a %s in square brackets, such as %s." % (lstr, example))
     else:
-        if (
-            inp == "[]"
-        ):  # fixes bug in the code below (split never returns an empty list)
+        if inp == "[]":  # fixes bug in the code below (split never returns an empty list)
             if split:
                 query[qfield] = []
             else:
@@ -1135,10 +1014,7 @@ def parse_bracketed_rats(
         if extractor is not None:
             for qf, v in zip(qfield, extractor(L)):
                 if qf in query and query[qf] != v:
-                    raise SearchParsingError(
-                        "Inconsistent specification of %s: %s vs %s"
-                        % (qf, query[qf], v)
-                    )
+                    raise SearchParsingError(f"Inconsistent specification of {qf}: {query[qf]} vs {v}")
                 query[qf] = v
         elif split:
             query[qfield] = L
@@ -1150,7 +1026,6 @@ def parse_bracketed_rats(
             else:
                 query[qfield] = inp[1:-1]
 
-
 def parse_gap_id(info, query, field="group", name="Group", qfield="group"):
     parse_bracketed_posints(
         info,
@@ -1163,14 +1038,14 @@ def parse_gap_id(info, query, field="group", name="Group", qfield="group"):
         qfield=qfield,
     )
 
-
+# see SearchParser.__call__ for actual arguments when calling
 @search_parser(
     clean_info=True,
     default_field="galois_group",
     default_name="Galois group",
     default_qfield="galois",
     error_is_safe=True,
-)  # see SearchParser.__call__ for actual arguments when calling
+)
 def parse_galgrp(inp, query, qfield, err_msg=None, list_ok=True):
     try:
         if list_ok:
@@ -1201,20 +1076,18 @@ def parse_galgrp(inp, query, qfield, err_msg=None, list_ok=True):
         if err_msg:
             raise SearchParsingError(err_msg)
         else:
-            raise SearchParsingError(
-                "It needs to be a list made up of GAP id's, such as [4,1] or [12,5], transitive groups in nTj notation, such as 5T1, and <a title = 'Galois group labels' knowl='nf.galois_group.name'>group labels</a>"
-            )
-
+            raise SearchParsingError("It needs to be a list made up of GAP id's, such as [4,1] or [12,5], transitive groups in nTj notation, such as 5T1, and <a title = 'Galois group labels' knowl='nf.galois_group.name'>group labels</a>")
 
 # The queries for inertia (and wild inertia) subgroups are different
 # than the ones for Galois groups.
+# see SearchParser.__call__ for actual arguments when calling
 @search_parser(
     clean_info=True,
     default_field="inertia_gap",
     default_name="Inertia group",
     default_qfield="inertia_gap",
     error_is_safe=True,
-)  # see SearchParser.__call__ for actual arguments when calling
+)
 def parse_inertia(inp, query, qfield, err_msg=None):
     try:
         # For inertia groups, qfield=('inertia_gap', 'inertia')
@@ -1256,24 +1129,17 @@ def parse_inertia(inp, query, qfield, err_msg=None):
         if err_msg:
             raise SearchParsingError(err_msg)
         else:
-            raise SearchParsingError(
-                "It needs to be a GAP id, such as [4,1] or [12,5], ia transitive group in nTj notation, such as 5T1, or a <a title = 'Group label' knowl='nf.galois_group.name'>group label</a>"
-            )
+            raise SearchParsingError("It needs to be a GAP id, such as [4,1] or [12,5], ia transitive group in nTj notation, such as 5T1, or a <a title = 'Group label' knowl='nf.galois_group.name'>group label</a>")
 
-
-@search_parser(
-    clean_info=True, error_is_safe=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True, error_is_safe=True)
 def parse_padicfields(inp, query, qfield):
     labellist = inp.split(",")
     for label in labellist:
         if not LF_LABEL_RE.match(label):
-            raise SearchParsingError(
-                'It needs to be a <a title = "$p$-adic field label" knowl="lf.field.label">$p$-adic field label</a> or a list of local field labels'
-            )
+            raise SearchParsingError('It needs to be a <a title = "$p$-adic field label" knowl="lf.field.label">$p$-adic field label</a> or a list of local field labels')
     query[qfield] = {"$contains": labellist}
 
-
 def input_string_to_poly(FF):
     # Change unicode dash with minus sign
     FF = FF.replace("\u2212", "-")
@@ -1288,7 +1154,6 @@ def input_string_to_poly(FF):
     except Exception:
         return None, F, FF
 
-
 def nf_string_to_label(FF):  # parse Q, Qsqrt2, Qsqrt-4, Qzeta5, etc
     if FF in ["q", "Q"]:
         return "1.1.1.1"
@@ -1297,20 +1162,14 @@ def nf_string_to_label(FF):  # parse Q, Qsqrt2, Qsqrt-4, Qzeta5, etc
 
     F1, F, FF = input_string_to_poly(FF)
     if len(F) == 0:
-        raise SearchParsingError(
-            "Entry for the field was left blank.  You need to enter a field label, field name, or a polynomial."
-        )
-    if (
-        F1 and len(str(F1.parent().gen())) == 1
-    ):  # we only support single-letter variable names
+        raise SearchParsingError("Entry for the field was left blank.  You need to enter a field label, field name, or a polynomial.")
+    if F1 and len(str(F1.parent().gen())) == 1: # we only support single-letter variable names
         from lmfdb.number_fields.number_field import poly_to_field_label
 
         F1 = poly_to_field_label(F1)
         if F1:
             return F1
-        raise SearchParsingError(
-            "%s does not define a number field in the database." % F
-        )
+        raise SearchParsingError("%s does not define a number field in the database." % F)
 
     if F[0] == "q":
         if "(" in F and ")" in F:
@@ -1321,11 +1180,7 @@ def nf_string_to_label(FF):  # parse Q, Qsqrt2, Qsqrt-4, Qzeta5, etc
             except (TypeError, ValueError):
                 d = 0
             if d == 0:
-                raise SearchParsingError(
-                    "After {0}, the remainder must be a nonzero integer.  Use {0}5 or {0}-11 for example.".format(
-                        FF[:5]
-                    )
-                )
+                raise SearchParsingError("After {0}, the remainder must be a nonzero integer.  Use {0}5 or {0}-11 for example.".format(FF[:5]))
             if d == 1:
                 return "1.1.1.1"
             if d % 4 in [2, 3]:
@@ -1340,11 +1195,7 @@ def nf_string_to_label(FF):  # parse Q, Qsqrt2, Qsqrt-4, Qzeta5, etc
                 F = F.replace("_", "")
             match_obj = re.match(r"^qzeta(\d+)(\+|plus)?$", F)
             if not match_obj:
-                raise SearchParsingError(
-                    "After {0}, the remainder must be a positive integer or a positive integer followed by '+'.  Use {0}5 or {0}19+, for example.".format(
-                        F[:5]
-                    )
-                )
+                raise SearchParsingError("After {0}, the remainder must be a positive integer or a positive integer followed by '+'.  Use {0}5 or {0}19+, for example.".format(F[:5]))
 
             d = ZZ(str(match_obj.group(1)))
             if d % 4 == 2:
@@ -1356,25 +1207,21 @@ def nf_string_to_label(FF):  # parse Q, Qsqrt2, Qsqrt-4, Qzeta5, etc
                 if d in rcyclolookup:
                     return rcyclolookup[d]
                 else:
-                    raise SearchParsingError("%s is not in the database." % F)
+                    raise SearchParsingError(f"{F} is not in the database.")
             # Now not the totally real subfield
             from lmfdb.number_fields.web_number_field import cyclolookup
 
             if d in cyclolookup:
                 return cyclolookup[d]
             else:
-                raise SearchParsingError("%s is not in the database." % F)
-        raise SearchParsingError(
-            "It is not a valid field name or label, or a defining polynomial."
-        )
+                raise SearchParsingError(f"{F} is not in the database.")
+        raise SearchParsingError("It is not a valid field name or label, or a defining polynomial.")
 
     # Expand out factored labels, like 11.11.11e20.1
     from lmfdb.number_fields.number_field import FIELD_LABEL_RE
 
     if not FIELD_LABEL_RE.match(F):
-        raise SearchParsingError(
-            "A number field label must be of the form d.r.D.n, such as 2.2.5.1."
-        )
+        raise SearchParsingError("A number field label must be of the form d.r.D.n, such as 2.2.5.1.")
     parts = F.split(".")
 
     def raise_power(ab):
@@ -1384,14 +1231,11 @@ def raise_power(ab):
             a, b = ab.split("e")
             return ZZ(a) ** ZZ(b)
         else:
-            raise SearchParsingError(
-                "Malformed absolute discriminant.  It must be a sequence of strings AeB for A and B integers, joined by _s.  For example, 2e7_3e5_11."
-            )
+            raise SearchParsingError("Malformed absolute discriminant.  It must be a sequence of strings AeB for A and B integers, joined by _s.  For example, 2e7_3e5_11.")
 
     parts[2] = str(prod(raise_power(c) for c in parts[2].split("_")))
     return ".".join(parts)
 
-
 # Similar to parsing a number field name, but with different output,
 # here coefficients low to high separated by .,
 # and different behavior if the entry is not in the database
@@ -1400,9 +1244,7 @@ def finish(result):
         return ".".join(str(z) for z in result)
 
     def notq():
-        raise SearchParsingError(
-            r"The rational numbers $\Q$ cannot be a proper intermediate field."
-        )
+        raise SearchParsingError(r"The rational numbers $\Q$ cannot be a proper intermediate field.")
 
     # Change unicode dash with minus sign
     inp = inp.replace("\u2212", "-")
@@ -1423,9 +1265,7 @@ def notq():
         if myfield:
             return finish(myfield["coeffs"])
         else:
-            raise SearchParsingError(
-                "It is not the label for a subfield in the database."
-            )
+            raise SearchParsingError("It is not the label for a subfield in the database.")
 
     F = inp.lower()  # keep original if needed
     # Is it a polynomial
@@ -1460,11 +1300,7 @@ def notq():
             except (TypeError, ValueError):
                 d = 0
             if d == 0 or d == 1:
-                raise SearchParsingError(
-                    "After {0}, the remainder must be a nonzero integer which is not a perfect square.  Use {0}5 or {0}-11 for example.".format(
-                        inp[:5]
-                    )
-                )
+                raise SearchParsingError("After {0}, the remainder must be a nonzero integer which is not a perfect square.  Use {0}5 or {0}-11 for example.".format(inp[:5]))
             # Recursion has it use polredabs to get the polynomial
             return input_to_subfield("x^2 - (%s)" % d)
         # Look for cyclotomic
@@ -1473,21 +1309,13 @@ def notq():
                 F = F.replace("_", "")
             match_obj = re.match(r"^qzeta(\d+)(\+|plus)?$", F)
             if not match_obj:
-                raise SearchParsingError(
-                    "After {0}, the remainder must be a positive integer or a positive integer followed by '+'.  Use {0}5 or {0}19+, for example.".format(
-                        F[:5]
-                    )
-                )
+                raise SearchParsingError("After {0}, the remainder must be a positive integer or a positive integer followed by '+'.  Use {0}5 or {0}19+, for example.".format(F[:5]))
 
             d = ZZ(str(match_obj.group(1)))
             if d % 4 == 2:
                 d /= 2  # Q(zeta_6)=Q(zeta_3), etc)
             if d < 1:
-                raise SearchParsingError(
-                    "After {0}, the remainder must be a positive integer or a positive integer followed by '+'.  Use {0}5 or {0}19+, for example.".format(
-                        F[:5]
-                    )
-                )
+                raise SearchParsingError("After {0}, the remainder must be a positive integer or a positive integer followed by '+'.  Use {0}5 or {0}19+, for example.".format(F[:5]))
             if d == 1:  # asking for Q
                 notq()
 
@@ -1506,10 +1334,7 @@ def notq():
                 raise SearchParsingError("%s is not in the database." % F)
             f = pari.polcyclo(d)
             return input_to_subfield(str(f))
-    raise SearchParsingError(
-        "It is not a valid field nickname or label, or a defining polynomial."
-    )
-
+    raise SearchParsingError("It is not a valid field nickname or label, or a defining polynomial.")
 
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_subfield(inp, query, qfield):
@@ -1517,12 +1342,10 @@ def parse_subfield(inp, query, qfield):
     if sf:  # Might return none
         query[qfield] = {"$contains": sf}
 
-
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_nf_string(inp, query, qfield):
     query[qfield] = nf_string_to_label(inp)
 
-
 def pol_string_to_list(pol, deg=None, var=None):
     if var is None:
         from lmfdb.hilbert_modular_forms.hilbert_field import findvar
@@ -1540,26 +1363,19 @@ def pol_string_to_list(pol, deg=None, var=None):
         fill = deg - pol.degree() - 1
     return [str(c) for c in pol.coefficients(sparse=False)] + ["0"] * fill
 
-
-@search_parser(
-    pass_name=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(pass_name=True)
 def parse_nf_elt(inp, query, name, qfield, field_label):
     if field_label is None:
         raise SearchParsingError("You must specify a field when searching by %s" % name)
     deg = int(field_label.split(".")[0])
     if "," in inp:
-        collapse_ors(
-            ["$or", [{qfield: pol_string_to_list(f, deg=deg)} for f in inp.split(",")]],
-            query,
-        )
+        collapse_ors(["$or", [{qfield: pol_string_to_list(f, deg=deg)} for f in inp.split(",")]], query)
     else:
         query[qfield] = pol_string_to_list(inp, deg=deg)
 
-
-@search_parser(
-    pass_name=True, clean_info=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(pass_name=True, clean_info=True)
 def parse_nf_jinv(inp, query, name, qfield, field_label):
     if field_label is not None:
         field_label = field_label.strip()
@@ -1574,41 +1390,25 @@ def parse_nf_jinv(inp, query, name, qfield, field_label):
             opts = [{qfield: {"$regex": "^%s(,0)*$" % QQ(x)}} for x in inp.split(",")]
             collapse_ors(["$or", opts], query)
         else:
-            raise SearchParsingError(
-                "With no field specified, it needs to be a rational number or comma separated list of rationals."
-            )
+            raise SearchParsingError("With no field specified, it needs to be a rational number or comma separated list of rationals.")
     elif field_label is None:
         raise SearchParsingError("You must specify a field when searching by %s" % name)
     else:
         deg = int(field_label.split(".")[0])
         if "," in inp:
-            collapse_ors(
-                [
-                    "$or",
-                    [
-                        {qfield: ",".join(pol_string_to_list(f, deg=deg))}
-                        for f in inp.split(",")
-                    ],
-                ],
-                query,
-            )
+            collapse_ors(["$or", [{qfield: ",".join(pol_string_to_list(f, deg=deg))} for f in inp.split(",")]], query)
         else:
             query[qfield] = ",".join(pol_string_to_list(inp, deg=deg))
 
-
-@search_parser(
-    clean_info=True
-)  # see SearchParser.__call__ for actual arguments when calling
+# see SearchParser.__call__ for actual arguments when calling
+@search_parser(clean_info=True)
 def parse_container(inp, query, qfield):
     inp = inp.replace("T", "t")
     format_ok = re.match(r"^\d+(t\d+)?$", inp)
     if format_ok:
         query[qfield] = str(inp)
     else:
-        raise SearchParsingError(
-            "You must specify a permutation representation, such as 6T13"
-        )
-
+        raise SearchParsingError("You must specify a permutation representation, such as 6T13")
 
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_hmf_weight(inp, query, qfield):
@@ -1619,10 +1419,7 @@ def parse_hmf_weight(inp, query, qfield):
         try:
             query[normal_field] = str(split_list(inp))
         except ValueError:
-            raise SearchParsingError(
-                "It must be either an integer (parallel weight) or a comma separated list of integers enclosed in brackets, such as 2, or [2,2], or [2,4,6]."
-            )
-
+            raise SearchParsingError("It must be either an integer (parallel weight) or a comma separated list of integers enclosed in brackets, such as 2, or [2,2], or [2,4,6].")
 
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_bool(inp, query, qfield, process=None, blank=[]):
@@ -1630,12 +1427,8 @@ def parse_bool(inp, query, qfield, process=None, blank=[]):
         return
     if process is None:
         process = lambda x: x
-    if inp in [
-        "True",
-        "yes",
-        "1",
-        "even",
-    ]:  # artin reps use parse_bool for an is_even parity field
+    if inp in ["True", "yes", "1", "even"]:
+        # artin reps use parse_bool for an is_even parity field
         query[qfield] = process(True)
     elif inp in ["False", "no", "-1", "0", "odd"]:
         query[qfield] = process(False)
@@ -1645,7 +1438,6 @@ def parse_bool(inp, query, qfield, process=None, blank=[]):
     else:
         raise SearchParsingError("It must be True or False.")
 
-
 @search_parser  # see SearchParser.__call__ for actual arguments when calling
 def parse_bool_unknown(inp, query, qfield):
     # yes, no, not_no, not_yes, unknown
@@ -1660,7 +1452,6 @@ def parse_bool_unknown(inp, query, qfield):
     elif inp == "unknown":
         query[qfield] = 0
 
-
 @search_parser
 def parse_restricted(inp, query, qfield, allowed, process=None, blank=[]):
     if inp in blank:
@@ -1680,11 +1471,8 @@ def parse_restricted(inp, query, qfield, allowed, process=None, blank=[]):
         raise SearchParsingError("It must be %s" % allowed_str)
     query[qfield] = process(inp)
 
-
 @search_parser
-def parse_regex_restricted(
-    inp, query, qfield, regex, err=None, errknowl=None, errtitle=None, comma_split=True
-):
+def parse_regex_restricted(inp, query, qfield, regex, err=None, errknowl=None, errtitle=None, comma_split=True):
     if comma_split:
         inps = inp.split(",")
     else:
@@ -1698,44 +1486,33 @@ def parse_regex_restricted(
         if err is not None:
             raise SearchParsingError(err)
         elif errknowl is not None and errtitle is not None:
-            raise SearchParsingError(
-                'It needs to be a <a title = "{0}" knowl="{1}">{0}</a> or a list of {0}s'.format(
-                    errtitle, errknowl
-                )
-            )
+            raise SearchParsingError('It needs to be a <a title = "{0}" knowl="{1}">{0}</a> or a list of {0}s'.format(errtitle, errknowl))
         else:
             raise SearchParsingError("It does not match the required form")
 
-
 @search_parser
 def parse_noop(inp, query, qfield, func=None):
     if func is not None:
         inp = func(inp)
     query[qfield] = inp
 
-
 @search_parser
-def parse_equality_constraints(
-    inp, query, qfield, prefix="a", parse_singleton=int, nshift=None
-):  # Note that postgres -> index is one-based
+def parse_equality_constraints(inp, query, qfield, prefix="a", parse_singleton=int, nshift=None):
+    # For nshift, note that postgres -> index is one-based
     for piece in inp.split(","):
         piece = piece.strip().split("=")
         if len(piece) != 2:
-            raise SearchParsingError(
-                "It must be a comma separated list of expressions of the form %sN=T"
-                % (prefix)
-            )
+            raise SearchParsingError(f"It must be a comma separated list of expressions of the form {prefix}N=T")
         n, t = piece
         n = n.strip()
         if not n.startswith(prefix):
-            raise SearchParsingError("%s does not start with %s" % (n, prefix))
+            raise SearchParsingError(f"{n} does not start with {prefix}")
         n = int(n[len(prefix) :])
         if nshift is not None:
             n = nshift(n)
         t = parse_singleton(t.strip())
         query[qfield + ".%s" % n] = t
 
-
 def parse_paired_fields(
     info,
     query,
@@ -1793,7 +1570,6 @@ def combine(D1, D2):
     else:
         collapse_ors(["$or", L], query)
 
-
 @search_parser
 def parse_list_start(inp, query, qfield, index_shift=0, parse_singleton=int):
     bparts = BRACKETING_RE.split(inp)
@@ -1824,28 +1600,18 @@ def make_sub_query(part):
             parsed_values = list(sub_query.values())
             # asking for each value to be in the array
             if parse_singleton is str:
-                all_operand = [
-                    val
-                    for val in parsed_values
-                    if type(val) == parse_singleton
-                    and "-" not in val
-                    and "," not in val
-                ]
+                all_operand = [val for val in parsed_values
+                               if type(val) == parse_singleton and "-" not in val and "," not in val]
             else:
-                all_operand = [
-                    val for val in parsed_values if type(val) == parse_singleton
-                ]
+                all_operand = [val for val in parsed_values if type(val) == parse_singleton]
 
             if all_operand:
                 sub_query[qfield] = {"$all": all_operand}
 
             # if there are other condition, we can add the first of those
             # conditions the query, in the hope of reducing the search space
-            elemMatch_operand = [
-                val
-                for val in parsed_values
-                if type(val) != parse_singleton and type(val) is dict
-            ]
+            elemMatch_operand = [val for val in parsed_values
+                                 if type(val) != parse_singleton and type(val) is dict]
             if elemMatch_operand:
                 if qfield in sub_query:
                     sub_query[qfield]["$elemMatch"] = elemMatch_operand[0]
@@ -1865,7 +1631,6 @@ def make_sub_query(part):
     else:
         collapse_ors(["$or", [make_sub_query(part) for part in parts]], query)
 
-
 @search_parser
 def parse_string_start(
     inp,
@@ -1918,9 +1683,7 @@ def make_sub_query(part):
         else:
             if "-" in part[1:]:
                 raise SearchParsingError("Ranges not supported.")
-            sub_query[qfield] = {
-                "$startswith": "%s %s " % (" ".join(initial_segment), part)
-            }
+            sub_query[qfield] = {"$startswith": "%s %s " % (" ".join(initial_segment), part)}
         return sub_query
 
     if len(parts) == 1:
@@ -1928,7 +1691,6 @@ def make_sub_query(part):
     else:
         collapse_ors(["$or", [make_sub_query(part) for part in parts]], query)
 
-
 def parse_count(info, default=50):
     try:
         info["count"] = int(info["count"])
@@ -1936,7 +1698,6 @@ def parse_count(info, default=50):
         info["count"] = default
     return info["count"]
 
-
 def parse_start(info, default=0):
     try:
         start = int(info["start"])


Test patch: