From: Thomas Stephenson Date: Fri, 16 May 2025 03:13:19 +0000 (+1000) Subject: Fix errors from initial CE run X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e07097ddd297a12082dc8e64661fefe338b1cfa1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Fix errors from initial CE run - fix mypy and linter issues - expand BitString tests for int and bytes conversion - Only run BIT test types on postgresql dialect - Fix problems in `BitString.from_int` and `BitString.from_bytes` - Add `BitString.to_bytes` method to allow specifying the byte length of the returned instances - Fixed problems testing operators in BIT type tests --- diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py index 12be23635d..9ca6428189 100644 --- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py +++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py @@ -247,11 +247,11 @@ class AsyncpgBit(BIT): asyncpg_BitString = dialect.dbapi.asyncpg.BitString def to_bind(value): - print(f'processing bound value \'{value}\'') + print(f"processing bound value '{value}'") if isinstance(value, str): value = BitString(value) r = asyncpg_BitString.from_int(int(value), len(value)) - print(f'returning {r}') + print(f"returning {r}") return r return value @@ -260,11 +260,8 @@ class AsyncpgBit(BIT): def result_processor(self, dialect, coltype): def to_result(value): if value is not None: - print(f'result {value} length {len(value)}') - value = BitString.from_int( - value.to_int(), - length=len(value) - ) + print(f"result {value} length {len(value)}") + value = BitString.from_int(value.to_int(), length=len(value)) return value return to_result diff --git a/lib/sqlalchemy/dialects/postgresql/bitstring.py b/lib/sqlalchemy/dialects/postgresql/bitstring.py index 803255d840..d796bcb696 100644 --- a/lib/sqlalchemy/dialects/postgresql/bitstring.py +++ b/lib/sqlalchemy/dialects/postgresql/bitstring.py @@ -1,4 +1,4 @@ -# dialects/postgresql/types.py +# dialects/postgresql/bitstring.py # Copyright (C) 2013-2025 the SQLAlchemy authors and contributors # # @@ -7,9 +7,12 @@ from __future__ import annotations import functools +import itertools import math -from typing import SupportsIndex, cast +from typing import Any +from typing import cast from typing import Literal +from typing import SupportsIndex @functools.total_ordering @@ -20,16 +23,15 @@ class BitString(str): b = BitString('101') """ - def __new__(cls, _value: str, _check=True): + def __new__(cls, _value: str, _check: bool = True) -> BitString: if not isinstance(_value, BitString) and ( _check and _value and any(c not in "01" for c in _value) ): - print(f'value: {_value}') raise ValueError("BitString must only contain '0' and '1' chars") return super().__new__(cls, _value) @classmethod - def from_int(cls, value: int, length: int): + def from_int(cls, value: int, length: int) -> BitString: """ Returns a BitString consisting of the bits in the little-endian representation of the given python int ``value``. A ``ValueError`` @@ -40,27 +42,22 @@ class BitString(str): will be padded on the left by ``'0'`` to bits to produce a """ if value < 0: - raise ValueError("value must be a postive integer") + raise ValueError("value must be non-negative") + if length < 0: + raise ValueError("length must be non-negative") - if length >= 0: - if length > 0: - template_str = f'{{0:0{length}b}}' if length > 0 else '' - r = template_str.format(value) - else: - # f'{0:00b}'.format(0) == '0' - r = '' - - if len(r) > length: - raise ValueError( - f"Cannot encode {value} as a BitString of length {length}" - ) - else: - r = '{0:b}'.format(value) + template_str = f"{{0:0{length}b}}" if length > 0 else "" + r = template_str.format(value) + + if (length == 0 and value > 0) or len(r) > length: + raise ValueError( + f"Cannot encode {value} as a BitString of length {length}" + ) return cls(r) @classmethod - def from_bytes(cls, value: bytes, length: int = -1): + def from_bytes(cls, value: bytes, length: int = -1) -> BitString: """ Returns a ``BitString`` consisting of the bits in the given ``value`` bytes. @@ -72,19 +69,20 @@ class BitString(str): cannot be represented in a string of this length, then a ``ValueError`` will be raised. """ - str_v: str = "".join(f"{c:08b}" for c in value) + str_v: str = "".join(f"{int(c):08b}" for c in value) if length >= 0: - str_v = str_v.lstrip('0') + str_v = str_v.lstrip("0") - if len(str_v) >= length: + if len(str_v) > length: raise ValueError( - f"Cannot encode {value} as a BitString of length {length}" + f"Cannot encode {value!r} as a BitString of " + f"length {length}" ) str_v = str_v.zfill(length) return cls(str_v) - def get_bit(self, index) -> Literal["0", "1"]: + def get_bit(self, index: int) -> Literal["0", "1"]: """ Returns the value of the flag at the given index @@ -93,14 +91,14 @@ class BitString(str): return cast(Literal["0", "1"], super().__getitem__(index)) @property - def bit_length(self): + def bit_length(self) -> int: return len(self) @property - def octet_length(self): + def octet_length(self) -> int: return math.ceil(len(self) / 8) - def has_bit(self, index) -> bool: + def has_bit(self, index: int) -> bool: return self.get_bit(index) == "1" def set_bit( @@ -121,154 +119,136 @@ class BitString(str): return self return BitString( - "".join([self[:index], value, self[index + 1:]]), False + "".join([self[:index], value, self[index + 1 :]]), False ) - # These methods probably should return str and not override the fillchar - # def ljust(self, width, fillchar=None) -> BitString: - # """ - # Returns the BitString left justified in a string of length width. - # Padding is done using the provided fillchar (default is '0'). - - # If the width is shorter than the length, then the original BitString - # is returned. - # """ - # if width < len(self): - # return self - - # fillchar = fillchar or "0" - # if str(fillchar) not in "01": - # raise ValueError("fillchar must be either '0' or '1'") - - # return BitString(super().ljust(width, fillchar or "0")) - - # def rjust(self, width, fillchar=None) -> BitString: - # if width < len(self): - # return self - - # fillchar = fillchar or "0" - # if str(fillchar) not in "01": - # raise ValueError("fillchar must be either '0' or '1'") - - # return BitString(super().rjust(width, fillchar)) - - def lstrip(self, char=None) -> BitString: + def lstrip(self, char: str | None = None) -> BitString: """ Returns a copy of the BitString with leading characters removed. If omitted or None, 'chars' defaults '0' e.g. - BitString('00010101000').lstrip() === BitString('00010101') - BitString('11110101111').lstrip('1') === BitString('1111010') + BitString('00010101000').lstrip() === BitString('00010101') + BitString('11110101111').lstrip('1') === BitString('1111010') """ if char is None: char = "0" return BitString(super().lstrip(char), False) - def rstrip(self, char=None) -> BitString: + def rstrip(self, char: str | None = "0") -> BitString: """ Returns a copy of the BitString with trailing characters removed. - If omitted or None, 'chars' trailing '0' + If omitted or None, 'char' defaults to "0" e.g. - BitString('00010101000').rstrip() === BitString('10101000') - BitString('11110101111').rstrip('1') === BitString('10101111') + BitString('00010101000').rstrip() === BitString('10101000') + BitString('11110101111').rstrip('1') === BitString('10101111') """ if char is None: char = "0" return BitString(super().rstrip(char), False) - def strip(self, char=None) -> BitString: + def strip(self, char: str | None = "0") -> BitString: """ Returns a copy of the BitString with both leading and trailing characters removed. - If ommitted or None, char defaults to '0' + If ommitted or None, char defaults to "0" e.g. - BitString('00010101000').rstrip() === BitString('10101') - BitString('11110101111').rstrip('1') === BitString('1010') + BitString('00010101000').rstrip() === BitString('10101') + BitString('11110101111').rstrip('1') === BitString('1010') """ if char is None: char = "0" return BitString(super().strip(char)) - def partition(self, sep: str = "0") -> tuple[BitString, str, BitString]: - """ - Split the string after the first appearance of sep - (which defaults to '0') and return a 3-tuple containing - the portion of the string before the separator. - - """ - prefix, _, suffix = super().partition(sep) - return (BitString(prefix, False), sep, BitString(suffix, False)) - def removeprefix(self, prefix: str, /) -> BitString: return BitString(super().removeprefix(prefix), False) def removesuffix(self, suffix: str, /) -> BitString: return BitString(super().removesuffix(suffix), False) - def replace(self, old, new, count: SupportsIndex = -1) -> BitString: + def replace( + self, + old: str, + new: str, + count: SupportsIndex = -1, + ) -> BitString: new = BitString(new) return BitString(super().replace(old, new, count=count), False) - def split( # type: ignore - self, - sep=None, - maxsplit: SupportsIndex = -1, - ) -> list[BitString]: + def split( + self, + sep: str | None = None, + maxsplit: SupportsIndex = -1, + ) -> list[str]: return [BitString(word) for word in super().split(sep, maxsplit)] - def zfill(self, width) -> BitString: + def zfill(self, width: SupportsIndex) -> BitString: return BitString(super().zfill(width), False) - def __repr__(self): + def __repr__(self) -> str: return f'BitString("{self.__str__()}")' - def __int__(self): + def __int__(self) -> int: return int(self, 2) if self else 0 - def __bytes__(self): + def to_bytes(self, length: int = -1) -> bytes: s = str(self) - bs = [] + bs: list[int] = [] while s: - bs.append(int(s[-8:], 2)) + bs.insert(0, int(s[-8:], 2)) s = s[:-8] + if length >= 0: + bs = list(itertools.dropwhile(lambda c: c == 0, bs)) + if len(bs) > length: + raise ValueError( + f"Cannot fit a BitString of length {len(self)} into a " + f"bytes instance of length {length}" + ) + # "zfill" the result with 0 bytes + bs = [0] * (length - len(bs)) + bs return bytes(bs) - def __lt__(self, o): + def __bytes__(self) -> bytes: + return self.to_bytes() + + def __lt__(self, o: object) -> bool: if isinstance(o, BitString): return super().__lt__(o) return NotImplemented - def __eq__(self, o): + def __eq__(self, o: object) -> bool: return isinstance(o, BitString) and super().__eq__(o) - def __hash__(self): + def __hash__(self) -> int: return hash(BitString) ^ super().__hash__() - def __getitem__(self, key): + def __getitem__(self, key: SupportsIndex | slice[Any, Any, Any]) -> str: return BitString(super().__getitem__(key), False) - def __add__(self, o): + def __add__(self, o: str) -> BitString: """Return self + o""" if not isinstance(o, str): - raise TypeError(( - "Can only concatenate str " - "(not '{0}') to BitString" - ).format(type(o))) - return BitString(''.join([self, o])) + raise TypeError( + ("Can only concatenate str (not '{0}') to BitString").format( + type(o) + ) + ) + return BitString("".join([self, o])) - def __radd__(self, o): + def __radd__(self, o: str) -> BitString: if not isinstance(o, str): - raise TypeError(( - "Can only concatenate str (not '{0}') to BitString" - ).format(type(o))) - return BitString(''.join([o, self])) + raise TypeError( + (f"Can only concatenate str (not '{0}') to BitString").format( + type(o) + ) + ) + return BitString("".join([o, self])) - def __lshift__(self, amount: int): + def __lshift__(self, amount: int) -> BitString: """ Shifts each the bitstring to the left by the given amount. String length is preserved. @@ -276,10 +256,10 @@ class BitString(str): i.e. BitString('000101') << 1 == BitString('001010') """ return BitString( - "".join([self, *("0" for _ in range(amount))])[-len(self):], False + "".join([self, *("0" for _ in range(amount))])[-len(self) :], False ) - def __rshift__(self, amount: int): + def __rshift__(self, amount: int) -> BitString: """ Shifts each bit in the bitstring to the right by the given amount. String length is preserved. @@ -288,7 +268,7 @@ class BitString(str): """ return BitString(self[:-amount], False).zfill(width=len(self)) - def __invert__(self): + def __invert__(self) -> BitString: """ Inverts (~) each bit in the bitstring @@ -296,7 +276,7 @@ class BitString(str): """ return BitString("".join("1" if x == "0" else "0" for x in self)) - def __and__(self, o): + def __and__(self, o: str) -> BitString: """ Performs a bitwise and (``&``) with the given operand. A ``ValueError`` is raised if the operand is not the same length. @@ -318,7 +298,7 @@ class BitString(str): False, ) - def __or__(self, o): + def __or__(self, o: str) -> BitString: """ Performs a bitwise or (``|``) with the given operand. A ``ValueError`` is raised if the operand is not the same length. @@ -340,7 +320,7 @@ class BitString(str): False, ) - def __xor__(self, o): + def __xor__(self, o: str) -> BitString: """ Performs a bitwise xor (``^``) with the given operand. A ``ValueError`` is raised if the operand is not the same length. diff --git a/lib/sqlalchemy/dialects/postgresql/types.py b/lib/sqlalchemy/dialects/postgresql/types.py index 4296df35e3..054111832e 100644 --- a/lib/sqlalchemy/dialects/postgresql/types.py +++ b/lib/sqlalchemy/dialects/postgresql/types.py @@ -15,16 +15,18 @@ from typing import Type from typing import TYPE_CHECKING from uuid import UUID as _python_UUID +from .bitstring import BitString from ...sql import sqltypes from ...sql import type_api from ...sql.type_api import TypeEngine -from .bitstring import BitString - if TYPE_CHECKING: from ...engine.interfaces import Dialect + from ...sql.operators import ColumnOperators from ...sql.operators import OperatorType + from ...sql.type_api import _BindProcessorType from ...sql.type_api import _LiteralProcessorType + from ...sql.type_api import _ResultProcessorType _DECIMAL_TYPES = (1231, 1700) _FLOAT_TYPES = (700, 701, 1021, 1022) @@ -273,45 +275,59 @@ class BIT(sqltypes.TypeEngine[BitString]): self.length = length or 1 self.varying = varying - def bind_processor(self, dialect): - def bound_value(value): + def bind_processor( + self, dialect: Dialect + ) -> _BindProcessorType[BitString]: + def bound_value(value: Any) -> Any: if isinstance(value, BitString): return str(value) return value + return bound_value - def result_processor(self, dialect, coltype): - def from_result_value(value): + def result_processor( + self, dialect: Dialect, coltype: object + ) -> _ResultProcessorType[BitString]: + def from_result_value(value: Any) -> Any: if value is not None: value = BitString(value) return value + return from_result_value - def coerce_compared_value(self, op, value) -> TypeEngine[Any]: + def coerce_compared_value( + self, op: OperatorType | None, value: Any + ) -> TypeEngine[Any]: if isinstance(value, str): return self return super().coerce_compared_value(op, value) @property - def python_type(self): + def python_type(self) -> type[Any]: return BitString class comparator_factory(TypeEngine.Comparator[BitString]): - def __lshift__(self, other: Any): + def __lshift__(self, other: Any) -> ColumnOperators: return self.bitwise_lshift(other) - def __rshift__(self, other: Any): + def __rshift__(self, other: Any) -> ColumnOperators: return self.bitwise_rshift(other) - def __and__(self, other: Any): + def __and__(self, other: Any) -> ColumnOperators: return self.bitwise_and(other) - def __or__(self, other: Any): + def __or__(self, other: Any) -> ColumnOperators: return self.bitwise_or(other) - def __invert__(self): + # __xor__ is not defined on sql.operators.ColumnOperators. + # Use `bitwise_xor` directly instead. + # def __xor__(self, other: Any) -> ColumnOperators: + # return self.bitwise_xor(other) + + def __invert__(self) -> ColumnOperators: return self.bitwise_not() + PGBit = BIT diff --git a/test/dialect/postgresql/test_bitstring.py b/test/dialect/postgresql/test_bitstring.py index 2a5b879c14..2cba388d3d 100644 --- a/test/dialect/postgresql/test_bitstring.py +++ b/test/dialect/postgresql/test_bitstring.py @@ -1,18 +1,18 @@ -from sqlalchemy.testing import fixtures - +from sqlalchemy import testing from sqlalchemy.dialects.postgresql import BitString +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.assertions import assert_raises from sqlalchemy.testing.assertions import eq_ from sqlalchemy.testing.assertions import is_false from sqlalchemy.testing.assertions import is_true -from sqlalchemy.testing.assertions import assert_raises class BitStringTests(fixtures.TestBase): + __only_on__ = "postgresql" - def test_ctor(self): + def test_str_conversion(self): x = BitString("1110111") eq_(str(x), "1110111") - eq_(int(x), 119) eq_(BitString("111"), BitString("111")) is_false(BitString("111") == "111") @@ -22,27 +22,67 @@ class BitStringTests(fixtures.TestBase): eq_(BitString("011")[1], BitString("1")) - def test_int_conversion(self): - assert_raises(ValueError, lambda: BitString.from_int(127, length=6)) - - eq_(BitString.from_int(127, length=8), BitString("01111111")) - eq_(int(BitString.from_int(127, length=8)), 127) - - eq_(BitString.from_int(119, length=10), BitString("0001110111")) - eq_(int(BitString.from_int(119, length=10)), 119) - - def test_bytes_conversion(self): - eq_(BitString.from_bytes(b"\x01"), BitString("0000001")) - eq_(BitString.from_bytes(b"\x01", 4), BitString("00000001")) - - eq_(BitString.from_bytes(b"\xaf\x04"), BitString("101011110010")) - eq_( - BitString.from_bytes(b"\xaf\x04", 12), - BitString("0000101011110010"), - ) - assert_raises( - ValueError, lambda: BitString.from_bytes(b"\xaf\x04", 4), 1 - ) + assert_raises(ValueError, lambda: BitString("1246")) + + @testing.combinations( + (0, 0, BitString("")), + (0, 1, BitString("0")), + (1, 1, BitString("1")), + (1, 0, ValueError), + (1, -1, ValueError), + (2, 1, ValueError), + (-1, 4, ValueError), + (1, 4, BitString("0001")), + (1, 10, BitString("0000000001")), + (127, 8, BitString("01111111")), + (127, 10, BitString("0001111111")), + (1404, 8, ValueError), + (1404, 12, BitString("010101111100")), + argnames="source, bitlen, result_or_error", + ) + def test_int_conversion(self, source, bitlen, result_or_error): + if isinstance(result_or_error, type): + assert_raises( + result_or_error, lambda: BitString.from_int(source, bitlen) + ) + return + + result = result_or_error + + bits = BitString.from_int(source, bitlen) + eq_(bits, result) + eq_(int(bits), source) + + @testing.combinations( + (b"", -1, BitString("")), + (b"", 4, BitString("0000")), + (b"\x00", 1, BitString("0")), + (b"\x01", 1, BitString("1")), + (b"\x01", 4, BitString("0001")), + (b"\x01", 10, BitString("0000000001")), + (b"\x01", -1, BitString("00000001")), + (b"\xff", 10, BitString("0011111111")), + (b"\xaf\x04", 8, ValueError), + (b"\xaf\x04", 16, BitString("1010111100000100")), + (b"\xaf\x04", 20, BitString("00001010111100000100")), + argnames="source, bitlen, result_or_error", + ) + def test_bytes_conversion(self, source, bitlen, result_or_error): + if isinstance(result_or_error, type): + assert_raises( + result_or_error, + lambda: BitString.from_bytes(source, length=bitlen), + ) + return + result = result_or_error + + bits = BitString.from_bytes(source, bitlen) + eq_(bits, result) + + # Expecting a roundtrip conversion in this case is nonsensical + if source == b"" and bitlen > 0: + return + eq_(bits.to_bytes(len(source)), source) def test_get_set_bit(self): eq_(BitString("1010").get_bit(2), "1") @@ -55,12 +95,6 @@ class BitStringTests(fixtures.TestBase): def test_string_methods(self): - # Which of these methods should be overridden to produce BitStrings? - eq_(BitString("111").center(8), " 111 ") - - eq_(BitString("0101").ljust(8), "0101 ") - eq_(BitString("0110").rjust(8), " 0110") - eq_(BitString("01100").lstrip(), BitString("1100")) eq_(BitString("01100").rstrip(), BitString("011")) eq_(BitString("01100").strip(), BitString("11")) @@ -83,7 +117,7 @@ class BitStringTests(fixtures.TestBase): eq_(BitString("0110").split("11"), [BitString("0"), BitString("0")]) eq_(BitString("111").zfill(8), BitString("00000111")) - def test_str_ops(self): + def test_string_operators(self): is_true("1" in BitString("001")) is_true("0" in BitString("110")) is_false("1" in BitString("000")) @@ -95,7 +129,7 @@ class BitStringTests(fixtures.TestBase): eq_(BitString("010") + "001", BitString("010001")) eq_("001" + BitString("010"), BitString("001010")) - def test_bitwise_ops(self): + def test_bitwise_operators(self): eq_(~BitString("0101"), BitString("1010")) eq_(BitString("010") & BitString("011"), BitString("010")) eq_(BitString("010") | BitString("011"), BitString("011")) diff --git a/test/dialect/postgresql/test_types.py b/test/dialect/postgresql/test_types.py index 6fa07a1094..3d3c93bc89 100644 --- a/test/dialect/postgresql/test_types.py +++ b/test/dialect/postgresql/test_types.py @@ -46,8 +46,8 @@ from sqlalchemy.dialects.postgresql import array from sqlalchemy.dialects.postgresql import array_agg from sqlalchemy.dialects.postgresql import asyncpg from sqlalchemy.dialects.postgresql import base -from sqlalchemy.dialects.postgresql import BitString from sqlalchemy.dialects.postgresql import BIT +from sqlalchemy.dialects.postgresql import BitString from sqlalchemy.dialects.postgresql import BYTEA from sqlalchemy.dialects.postgresql import CITEXT from sqlalchemy.dialects.postgresql import DATEMULTIRANGE @@ -3553,12 +3553,11 @@ class SpecialTypesTest(fixtures.TablesTest, ComparesTables): assert t.c.bitstring_varying.type.length is None assert t.c.bitstring_varying_6.type.varying is True - assert t.c.bitstring_varying_6.type.length == 6 + assert t.c.bitstring_varying_6.type.length == 6 assert t.c.bitstring_4.type.varying is False assert t.c.bitstring_4.type.length == 4 - @testing.combinations( (postgresql.INET, "127.0.0.1"), (postgresql.CIDR, "192.168.100.128/25"), @@ -3600,18 +3599,20 @@ class SpecialTypesTest(fixtures.TablesTest, ComparesTables): "bits", metadata, Column("name", String), - Column("value", column_type) + Column("value", column_type), ) t.create(connection) connection.execute(t.insert(), {"name": "test", "value": value}) - print('value type affinity', t.c.value.type._type_affinity) + print("value type affinity", t.c.value.type._type_affinity) eq_( connection.scalar(select(t.c.name).where(t.c.value == value)), "test", ) - result_value = connection.scalar(select(t.c.value).where(t.c.name == "test")) + result_value = connection.scalar( + select(t.c.value).where(t.c.name == "test") + ) assert isinstance(result_value, BitString) assert str(result_value) == str(value) @@ -4217,104 +4218,109 @@ class HStoreRoundTripTest(fixtures.TablesTest): class BitTests(fixtures.TestBase): + __dialect__ = "postgresql" + __only_on__ = "postgresql" + def test_concatenation(self, connection): coltype = BIT(varying=True) q = select( - literal(BitString('1111'), coltype).concat(BitString('0000')) + literal(BitString("1111"), coltype).concat(BitString("0000")) ) r = connection.execute(q).first() - eq_(r[0], BitString('11110000')) + eq_(r[0], BitString("11110000")) - @testing.skip("compiler bug") + @testing.skip_if("postgresql", "sql compiler bug") def test_invert_operator(self, connection): coltype = BIT(4) - q = select( - literal(BitString('0010'), coltype).bitwise_not() - ) + q = select(literal(BitString("0010"), coltype).bitwise_not()) r = connection.execute(q).first() # Observing r[0] == '1101' here. + # # See: sql.compiler.Compiler._label_select_column # The unary operator does not "wrap a column expression" # and it isn't a from clause of the select, # the compiler doesn't actually add the column to the select's # result_columns and thus the type's result_processor never gets # called. - eq_(r[0], BitString('1101')) + eq_(r[0], BitString("1101")) def test_and_operator(self, connection): coltype = BIT(6) q1 = select( - literal(BitString('001010'), coltype) - & literal(BitString('010111'), coltype) + literal(BitString("001010"), coltype) + & literal(BitString("010111"), coltype) ) r1 = connection.execute(q1).first() - eq_(r1[0], BitString('000010')) + eq_(r1[0], BitString("000010")) q2 = select( - literal(BitString('010101'), coltype) & BitString('001011') + literal(BitString("010101"), coltype) & BitString("001011") ) r2 = connection.execute(q2).first() - eq_(r2[0], BitString('000001')) + eq_(r2[0], BitString("000001")) def test_or_operator(self, connection): coltype = BIT(6) q1 = select( - literal(BitString('001010'), coltype) - & literal(BitString('010111'), coltype) + literal(BitString("001010"), coltype) + | literal(BitString("010111"), coltype) ) r1 = connection.execute(q1).first() - eq_(r1[0], BitString('011111')) + eq_(r1[0], BitString("011111")) q2 = select( - literal(BitString('010101')) & BitString('001001') + literal(BitString("010101"), coltype) | BitString("001011") ) r2 = connection.execute(q2).first() - eq_(r2[0], BitString('011101')) + eq_(r2[0], BitString("011111")) def test_xor_operator(self, connection): coltype = BIT(6) q1 = select( - literal(BitString('001010'), coltype) - & literal(BitString('010111'), coltype) + literal(BitString("001010"), coltype).bitwise_xor( + literal(BitString("010111"), coltype) + ) ) r1 = connection.execute(q1).first() - eq_(r1[0], BitString('001101')) + eq_(r1[0], BitString("011101")) q2 = select( - literal(BitString('010101'), coltype) & BitString('001011') + literal(BitString("010101"), coltype).bitwise_xor( + BitString("001011") + ) ) r2 = connection.execute(q2).first() - eq_(r2[0], BitString('011110')) + eq_(r2[0], BitString("011110")) def test_lshift_operator(self, connection): coltype = BIT(6) q = select( - literal(BitString('001010'), coltype), - literal(BitString('001010'), coltype) << 1, + literal(BitString("001010"), coltype), + literal(BitString("001010"), coltype) << 1, ) r = connection.execute(q).first() - eq_(tuple(r), (BitString('001010'), BitString('010100'))) + eq_(tuple(r), (BitString("001010"), BitString("010100"))) def test_rshift_operator(self, connection): coltype = BIT(6) q = select( - literal(BitString('001010'), coltype), - literal(BitString('001010'), coltype) >> 1 + literal(BitString("001010"), coltype), + literal(BitString("001010"), coltype) >> 1, ) r = connection.execute(q).first() - eq_(tuple(r), (BitString('001010'), BitString('000101'))) + eq_(tuple(r), (BitString("001010"), BitString("000101"))) class RangeMiscTests(fixtures.TestBase):