Repository: django/django
File: django/contrib/postgres/search.py (Python)

Code before:
class Lexeme(LexemeCombinable, Value):
    _output_field = SearchQueryField()

    def __init__(
        self, value, output_field=None, *, invert=False, prefix=False, weight=None
    ):
        if weight is not None and (
            not isinstance(weight, str) or weight.lower() not in {"a", "b", "c", "d"}
        ):
            raise ValueError(
                f"Weight must be one of 'A', 'B', 'C', and 'D', got '{weight}'."
            )

        self.prefix = prefix
        self.invert = invert
        self.weight = weight
        super().__init__(value, output_field=output_field)

    def process_rhs(self, compiler, connection):
        escaped_value = psql_escape(self.value)
        if is_psycopg3:
            from psycopg.adapt import Dumper

            class StringDumper(Dumper):
                def dump(self, obj):
                    return bytes(obj, "utf-8")

            param = StringDumper(str).quote(escaped_value).decode()
        else:
            from psycopg2.extensions import adapt

            param = adapt(escaped_value).getquoted().decode("latin-1")

        label = ""
        if self.prefix:
            label += "*"
        if self.weight:
            label += self.weight

        if label:
            param = f"{param}:{label}"
        if self.invert:
            param = f"!{param}"

        return "%s", (param,)

    def as_sql(self, compiler, connection):
        return self.process_rhs(compiler, connection)

    def __invert__(self):
        cloned = super().copy()

Review comment (bug):
`Lexeme("")` is valid in the psycopg2 branch, but fails here with TypeError. Can we avoid the TypeError? (~Or raise a ValueError in both branches, if the empty string makes no sense?~ On second thought, maybe that's heavy-handed...)

Code after:
    # it's consistent with other usage in Django.
    def __or__(self, other):
        return self._combine(other, self.BITOR, False)

    def __ror__(self, other):
        return self._combine(other, self.BITOR, True)

    def __and__(self, other):
        return self._combine(other, self.BITAND, False)

    def __rand__(self, other):
        return self._combine(other, self.BITAND, True)


class Lexeme(LexemeCombinable, Value):
    _output_field = SearchQueryField()

    def __init__(
        self, value, output_field=None, *, invert=False, prefix=False, weight=None
    ):
        if value == "":
            raise ValueError("Lexeme value cannot be empty.")

        if not isinstance(value, str):
            raise TypeError(
                f"Lexeme value must be a string, got {value.__class__.__name__}."
            )

        if weight is not None and (
            not isinstance(weight, str) or weight.lower() not in {"a", "b", "c", "d"}
        ):
            raise ValueError(
                f"Weight must be one of 'A', 'B', 'C', and 'D', got {weight!r}."
            )

        self.prefix = prefix
        self.invert = invert
        self.weight = weight
        super().__init__(value, output_field=output_field)

    def as_sql(self, compiler, connection):
        param = quote_lexeme(self.value)
        label = ""
        if self.prefix:
            label += "*"
        if self.weight:
            label += self.weight

        if label:
            param = f"{param}:{label}"
        if self.invert: