mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-06 17:01:07 -04:00
Code review fixes
- Removed dprint statements - Added right-hand versions of commutative operators - Improved docstrings - Added changelog entry - Added dialect documentation
This commit is contained in:
Vendored
+22
@@ -384,3 +384,25 @@ would appear in a valid ODBC connection string (i.e., the same as would be
|
||||
required if using the connection string directly with ``pyodbc.connect()``).
|
||||
|
||||
:ticket:`11250`
|
||||
|
||||
.. _change_12594
|
||||
|
||||
Addition of ``BitString`` subclass for handling postgresql ``BIT`` columns
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Values of :class:`_dialects.postgresl.BIT` columns in the postgresql dialect are
|
||||
now objects of a new :class:`str` subclass,
|
||||
:class:`_dialects.postgresql.BitString`. Previously, the value of :class:`BIT`
|
||||
columns was driver dependent, with most drivers using :class:`str` instances
|
||||
except asyncpg, which used :class:`asyncpg.BitString`.
|
||||
|
||||
For implementations using the `psycopg`, `psycopg2` or `pg8000` drivers, the new type is
|
||||
mostly compatible with :class:`str` with some additional methods for manipulating bits and
|
||||
implementations of the bitwise operators. However, equality and ordering
|
||||
value will now fail. This can be rectified by wrapping or unwrapping one side
|
||||
of the comparison so that both sides are similarly typed.
|
||||
|
||||
For implementations using the `asyncpg` driver, the new type is incompatible with
|
||||
the existing :class:`asyncpg.BitString` type.
|
||||
|
||||
:ticket:`10556`
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
.. change::
|
||||
:tags: feature, postgresql
|
||||
:tickets: 10556
|
||||
|
||||
Adds the :class:`dialects.postgresql.BitString` representing PostgreSQL bitstrings
|
||||
in python. This type, which is a subtype of the :class:`str` builtin includes
|
||||
functionality for converting to/from :class:`int` and :class:`bytes`, in
|
||||
addition to implementing utility methods and operators for dealing with bits.
|
||||
|
||||
In PostgreSQL drivers, the :class:`postgresql.BIT` type now binds to instances of
|
||||
this new utility class.
|
||||
Vendored
+15
@@ -374,6 +374,21 @@ don't yet fully support, conversion of rows to Python ``ipaddress`` datatypes
|
||||
|
||||
.. versionadded:: 2.0.18 - added the ``native_inet_types`` parameter.
|
||||
|
||||
PostgreSQL BIT type
|
||||
-------------------
|
||||
|
||||
The PostgreSQL dialect provides a :class:`_postgresql.BitString` type which
|
||||
represents an ordered sequence of boolean switches. This is exposed in python as a
|
||||
subclass of :class:`str` which exposes appropriate bitwise methods and operators.
|
||||
|
||||
* :class:`_postgrsql.BIT` - Typing support for PostgreSQL bitstrings.
|
||||
|
||||
* :class:`_postgresql.BitString` - Python implementation of postgresql bitstrings
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
PostgreSQL supports
|
||||
|
||||
PostgreSQL Data Types
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -247,11 +247,9 @@ class AsyncpgBit(BIT):
|
||||
asyncpg_BitString = dialect.dbapi.asyncpg.BitString
|
||||
|
||||
def to_bind(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}")
|
||||
return r
|
||||
return value
|
||||
|
||||
@@ -260,7 +258,6 @@ 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))
|
||||
return value
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import math
|
||||
from typing import Any
|
||||
from typing import cast
|
||||
@@ -17,29 +16,26 @@ from typing import SupportsIndex
|
||||
|
||||
@functools.total_ordering
|
||||
class BitString(str):
|
||||
"""Represent a PostgreSQL bit string.
|
||||
"""Represent a PostgreSQL bit string in python"""
|
||||
|
||||
e.g.
|
||||
b = BitString('101')
|
||||
"""
|
||||
_DIGITS = frozenset("01")
|
||||
|
||||
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)
|
||||
):
|
||||
do_check = not isinstance(_value, BitString) or _check
|
||||
if do_check and cls._DIGITS.union(_value) > cls._DIGITS:
|
||||
raise ValueError("BitString must only contain '0' and '1' chars")
|
||||
return super().__new__(cls, _value)
|
||||
|
||||
@classmethod
|
||||
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``
|
||||
is raised if ``value`` is not a non-negative integer.
|
||||
Returns a BitString consisting of the bits in the integer ``value``.
|
||||
A ``ValueError`` is raised if ``value`` is not a non-negative integer.
|
||||
|
||||
If the provided ``value`` can not be represented in a bit string
|
||||
of at most ``length``, a ``ValueError`` will be raised. The bitstring
|
||||
will be padded on the left by ``'0'`` to bits to produce a
|
||||
bitstring of the desired length.
|
||||
"""
|
||||
if value < 0:
|
||||
raise ValueError("value must be non-negative")
|
||||
@@ -86,7 +82,9 @@ class BitString(str):
|
||||
"""
|
||||
Returns the value of the flag at the given index
|
||||
|
||||
e.g. BitString('0101').get_flag(4) == 1
|
||||
e.g.::
|
||||
|
||||
BitString("0101").get_flag(4) == "1"
|
||||
"""
|
||||
return cast(Literal["0", "1"], super().__getitem__(index))
|
||||
|
||||
@@ -196,21 +194,7 @@ class BitString(str):
|
||||
return int(self, 2) if self else 0
|
||||
|
||||
def to_bytes(self, length: int = -1) -> bytes:
|
||||
s = str(self)
|
||||
bs: list[int] = []
|
||||
while s:
|
||||
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)
|
||||
return int(self).to_bytes(length if length >= 0 else self.octet_length)
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self.to_bytes()
|
||||
@@ -226,25 +210,23 @@ class BitString(str):
|
||||
def __hash__(self) -> int:
|
||||
return hash(BitString) ^ super().__hash__()
|
||||
|
||||
def __getitem__(self, key: SupportsIndex | slice[Any, Any, Any]) -> str:
|
||||
def __getitem__(
|
||||
self, key: SupportsIndex | slice[Any, Any, Any]
|
||||
) -> BitString:
|
||||
return BitString(super().__getitem__(key), False)
|
||||
|
||||
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)
|
||||
)
|
||||
f"Can only concatenate str (not '{type(self)}') to BitString"
|
||||
)
|
||||
return BitString("".join([self, o]))
|
||||
|
||||
def __radd__(self, o: str) -> BitString:
|
||||
if not isinstance(o, str):
|
||||
raise TypeError(
|
||||
(f"Can only concatenate str (not '{0}') to BitString").format(
|
||||
type(o)
|
||||
)
|
||||
f"Can only concatenate str (not '{type(self)}') to BitString"
|
||||
)
|
||||
return BitString("".join([o, self]))
|
||||
|
||||
@@ -253,7 +235,8 @@ class BitString(str):
|
||||
Shifts each the bitstring to the left by the given amount.
|
||||
String length is preserved.
|
||||
|
||||
i.e. BitString('000101') << 1 == BitString('001010')
|
||||
e.g.::
|
||||
BitString("000101") << 1 == BitString("001010")
|
||||
"""
|
||||
return BitString(
|
||||
"".join([self, *("0" for _ in range(amount))])[-len(self) :], False
|
||||
@@ -264,7 +247,8 @@ class BitString(str):
|
||||
Shifts each bit in the bitstring to the right by the given amount.
|
||||
String length is preserved.
|
||||
|
||||
e.g. BitString('101') >> 1 == BitString('010')
|
||||
e.g.::
|
||||
BitString("101") >> 1 == BitString("010")
|
||||
"""
|
||||
return BitString(self[:-amount], False).zfill(width=len(self))
|
||||
|
||||
@@ -272,7 +256,8 @@ class BitString(str):
|
||||
"""
|
||||
Inverts (~) each bit in the bitstring
|
||||
|
||||
e.g. ~BitString('01010') == BitString('10101')
|
||||
e.g.::
|
||||
~BitString("01010") == BitString("10101")
|
||||
"""
|
||||
return BitString("".join("1" if x == "0" else "0" for x in self))
|
||||
|
||||
@@ -281,7 +266,8 @@ class BitString(str):
|
||||
Performs a bitwise and (``&``) with the given operand.
|
||||
A ``ValueError`` is raised if the operand is not the same length.
|
||||
|
||||
e.g. BitString('011') & BitString('011') == BitString('010')
|
||||
e.g.::
|
||||
BitString("011") & BitString("011") == BitString("010")
|
||||
"""
|
||||
|
||||
if not isinstance(o, str):
|
||||
@@ -303,7 +289,8 @@ class BitString(str):
|
||||
Performs a bitwise or (``|``) with the given operand.
|
||||
A ``ValueError`` is raised if the operand is not the same length.
|
||||
|
||||
e.g. BitString('011') | BitString('010') == BitString('011')
|
||||
e.g.::
|
||||
BitString("011") | BitString("010") == BitString("011")
|
||||
"""
|
||||
if not isinstance(o, str):
|
||||
return NotImplemented
|
||||
@@ -325,7 +312,8 @@ class BitString(str):
|
||||
Performs a bitwise xor (``^``) with the given operand.
|
||||
A ``ValueError`` is raised if the operand is not the same length.
|
||||
|
||||
e.g. BitString('011') ^ BitString('010') == BitString('001')
|
||||
e.g.::
|
||||
BitString("011") ^ BitString("010") == BitString("001")
|
||||
"""
|
||||
|
||||
if not isinstance(o, BitString):
|
||||
@@ -345,3 +333,7 @@ class BitString(str):
|
||||
),
|
||||
False,
|
||||
)
|
||||
|
||||
__rand__ = __and__
|
||||
__ror__ = __or__
|
||||
__rxor__ = __xor__
|
||||
|
||||
Reference in New Issue
Block a user