diff --git a/.docker/db-init.sql b/.docker/db-init.sql new file mode 100644 index 0000000..eee2d3a --- /dev/null +++ b/.docker/db-init.sql @@ -0,0 +1 @@ +CREATE DATABASE test; \ No newline at end of file diff --git a/.docker/wait-for-it.sh b/.docker/wait-for-it.sh new file mode 100644 index 0000000..d990e0d --- /dev/null +++ b/.docker/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..80b5541 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +# Docs +docs/ + +# Python cache files +**/__pycache__/ + +# Private and public keys +*.key +*.ppk +*.pub + +# Hidden apps directories +.github/ +.idea/ +.gitignore \ No newline at end of file diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 9311ad6..75a5026 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -11,27 +11,26 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] - postgres-version: [9.5, 9.6, 10, 11, 12] - django-version: [2.0, 2.1, 2.2, 3.0, 3.1] - exclude: - # Django 3.0+ doesn't support python 3.5 - - python-version: 3.5 - django-version: 3.0 - - python-version: 3.5 - django-version: 3.1 + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + postgres-version: ["9.6.17", "10.12", "11.7", "12.2", "13.2"] + django-version: ["2.1", "2.2", "3.0", "3.1", "3.2"] + + services: + postgres: + image: jasei/postgres-hll:${{ matrix.postgres-version }} + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 steps: - uses: actions/checkout@v2 - # Install postgres with hll extension. Container is better, but can't be installed with hll - - name: Install PostgreSQL - run: | - sudo apt-get install postgresql-${{ matrix.postgres-version }} postgresql-${{ matrix.postgres-version }}-hll - sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/${{ matrix.postgres-version }}/main/postgresql.conf - sudo sed -i -E 's/(local\s+all\s+postgres\s+)(peer)/\1trust/g' /etc/postgresql/${{ matrix.postgres-version }}/main/pg_hba.conf - sudo systemctl stop postgresql && sudo systemctl start postgresql@${{ matrix.postgres-version }}-main - - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: @@ -65,13 +64,15 @@ jobs: - name: Set up test databases run: | - psql -tc 'SHOW server_version' -U postgres - psql -c 'CREATE ROLE test;' -U postgres - psql -c 'ALTER ROLE test WITH SUPERUSER;' -U postgres - psql -c 'ALTER ROLE test WITH LOGIN;' -U postgres - psql -c "ALTER ROLE test PASSWORD 'test';" -U postgres - psql -c 'CREATE DATABASE test OWNER test;' -U postgres - + psql -tc 'SHOW server_version' -U postgres -h localhost + psql -c 'CREATE ROLE test;' -U postgres -h localhost + psql -c 'ALTER ROLE test WITH SUPERUSER;' -U postgres -h localhost + psql -c 'ALTER ROLE test WITH LOGIN;' -U postgres -h localhost + psql -c "ALTER ROLE test PASSWORD 'test';" -U postgres -h localhost + psql -c 'CREATE DATABASE test OWNER test;' -U postgres -h localhost + env: + PGPASSWORD: postgres + - name: Test with unittest run: | python runtests.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..444fe69 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +ARG PYTHON_IMAGE_TAG=latest + +FROM python:${PYTHON_IMAGE_TAG} AS image_stage + +ARG APP_TAG="2.1.0" + +LABEL \ + org.label-schema.build-date=Now \ + org.label-schema.maintainer="work_shvein_mihail@mail.ru" \ + org.label-schema.schema-version="1.0.0-rc1" \ + org.label-schema.vcs-ref="v${APP_TAG}" \ + org.label-schema.vcs-url="https://github.com/M1ha-Shvn/django-pg-hll" \ + org.label-schema.vendor="M1ha-Shvn" \ + org.label-schema.version="${APP_TAG}" + +ENV APP_UID ${APP_UID:-1000} +ENV APP_GID ${APP_GID:-1000} +ENV APP_NAME ${APP_NAME:-"app"} + +# Configure utf-8 locales to make sure Python correctly handles unicode filenames +# Configure pip local path to copy data from pip_stage +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 DJANGO_SETTINGS_MODULE=tests.settings PYTHONUSERBASE=/pip PATH=/pip/bin:$PATH + +RUN set -eu && \ + groupadd --gid "${APP_GID}" "app" && \ + useradd --uid ${APP_UID} --gid ${APP_GID} --create-home --shell /bin/bash -d /app app && \ + mkdir -p /pip && \ + chmod 755 /app /pip && \ + chown -R ${APP_UID}:${APP_GID} /app /pip + +WORKDIR /app/src + +# Install dependencies +# set -eu "breaks" pipeline on first error +COPY ./requirements-test.txt /app/requirements-test.txt +RUN --mount=type=cache,target=/root/.cache/pip \ + set -eu && \ + python3 -m pip install --upgrade pip setuptools wheel && \ + python3 -m pip install --upgrade --requirement /app/requirements-test.txt + +COPY . /app/src + +RUN python3 setup.py -q install --user + +USER ${APP_UID} + +CMD ["python3", "runtests.py"] diff --git a/README.md b/README.md index b45aeeb..a493aa4 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,10 @@ Provides a django wrapper for [postgresql-hll library by CitusData](https://github.com/citusdata/postgresql-hll#the-importance-of-hashing) ## Requirements -* Python Python 3.5+ -* django >= 1.9 (tested 2.0+) -* pytz -* six -* typing -* psycopg2 -* PostgreSQL 9.4+ (tested 9.5+) +* Python 3.5+ +* django >= 1.9 (tested 2.1+) +* psycopg2-binary +* PostgreSQL 9.4+ (tested 9.6+) ## Installation Install via pip: @@ -23,6 +20,14 @@ or via setup.py: ### Prerequisites Install [postgresql-hll extension](https://github.com/citusdata/postgresql-hll#install) +#### Creating hll extension +If your user has super-admin privileges you can create Hll extension using migrations. +If you use django 1.10+ you can use `django_pg_hll.migrations.HllExtension` in your migration file. +If you have older version you can use the following: +```python +migrations.RunSQL('CREATE EXTENSION IF NOT EXISTS hll;', reverse_sql='DROP EXTENSION hll;') +``` + ### Creating table with hll field * Add HllField to your model: ```python @@ -226,3 +231,27 @@ MyModel.objects.bulk_update_or_create([ ], set_functions={'hll_field': 'hll_concat'} ) ``` + + +## Running tests +### Running in docker +1. Install [docker and docker-compose](https://www.docker.com/) +2. Run `docker build . --tag django-pg-hll` in project directory +3. Run `docker-compose run run_tests` in project directory + +### Running in virtual environment +1. Install all requirements listed above +2. [Create virtual environment](https://docs.python.org/3/tutorial/venv.html) +3. Create a superuser named 'test' on your local Postgres instance: + ```sql + CREATE ROLE test; + ALTER ROLE test WITH SUPERUSER; + ALTER ROLE test WITH LOGIN; + ALTER ROLE test PASSWORD 'test'; + CREATE DATABASE test OWNER test; + ``` +3. Install requirements + `pip3 install -U -r requirements-test.txt` +4. Start tests + `python3 runtests.py` + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dd33370 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +--- +version: "3.9" +services: + postgres_db: + image: jasei/postgres-hll + environment: + - POSTGRES_PASSWORD=postgres + volumes: + - ./.docker/db-init.sql:/docker-entrypoint-initdb.d/db-init.sql + mem_limit: 1g + cpus: 1 + + run_tests: + image: django-pg-hll + build: + context: . + args: + - PYTHON_IMAGE_TAG=latest + volumes: + - ./.docker/wait-for-it.sh:/bin/wait-for-it.sh + command: ["/bin/bash", "/bin/wait-for-it.sh", "postgres_db:5432", "-s", "-t", "0", "--", "python3", "runtests.py"] + environment: + - PGHOST=postgres_db + - PGUSER=postgres + - PGPASS=postgres + depends_on: + - postgres_db + mem_limit: 1g + cpus: 1 diff --git a/requirements-test.txt b/requirements-test.txt index 008194d..3b7784f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,8 +1,7 @@ Django (>=1.7) -pytz -six -typing -psycopg2 +pytz; python_version < '3.3' +typing; python_version < '3.5' +psycopg2-binary # Not required, but should be tested django-pg-bulk-update diff --git a/requirements.txt b/requirements.txt index 73c6933..4e362ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ Django (>=1.7) -pytz -six -typing -psycopg2 \ No newline at end of file +pytz; python_version < '3.3' +typing; python_version < '3.5' +psycopg2-binary diff --git a/setup.py b/setup.py index 78dde26..f61f625 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='django-pg-hll', - version='2.0.0', + version='2.1.0', packages=['django_pg_hll'], package_dir={'': 'src'}, url='https://github.com/M1hacka/django-pg-hll', @@ -23,5 +23,5 @@ description='Provides a django wrapper for postgresql-hll library by CitusData', long_description=long_description, long_description_content_type="text/markdown", - requires=requires + install_requires=requires ) diff --git a/src/django_pg_hll/compatibility.py b/src/django_pg_hll/compatibility.py index 10e6a2c..e5dba94 100644 --- a/src/django_pg_hll/compatibility.py +++ b/src/django_pg_hll/compatibility.py @@ -1,3 +1,4 @@ +import sys def django_pg_bulk_update_available(): # type: () -> bool @@ -10,3 +11,15 @@ def django_pg_bulk_update_available(): # type: () -> bool return True except ImportError: return False + + +try: + # This approach applies to python 3.10+ + from collections.abc import Iterable # noqa F401 +except ImportError: + # This approach applies to python versions less than 3.10 + from collections import Iterable # noqa F401 + + +# six.string_types replacement in order to remove dependency +string_types = (str,) if sys.version_info[0] == 3 else (str, unicode) # noqa F821 diff --git a/src/django_pg_hll/fields.py b/src/django_pg_hll/fields.py index f2a8e76..1e826a6 100644 --- a/src/django_pg_hll/fields.py +++ b/src/django_pg_hll/fields.py @@ -2,10 +2,10 @@ This file contains a field to use in django models """ import re -import six from django.contrib.postgres.fields import ArrayField from django.db.models import BinaryField +from .compatibility import string_types from .values import HllEmpty, HllFromHex __all__ = ['HllField'] @@ -58,7 +58,7 @@ def get_db_prep_value(self, value, connection, prepared=False): # Psycopg2 returns Binary results as hex string, prefixed by \x # BinaryField requires bytes to be saved # But none of these can be converted to HLL by postgres directly - if isinstance(value, bytes) or isinstance(value, six.string_types) and value.startswith(r'\x'): + if isinstance(value, bytes) or isinstance(value, string_types) and value.startswith(r'\x'): return HllFromHex(value, db_type=self.db_type(connection)) else: return super(HllField, self).get_db_prep_value(value, connection, prepared=prepared) diff --git a/src/django_pg_hll/migration.py b/src/django_pg_hll/migration.py new file mode 100644 index 0000000..cb8dc0b --- /dev/null +++ b/src/django_pg_hll/migration.py @@ -0,0 +1,9 @@ +from django.contrib.postgres.operations import CreateExtension + + +class HllExtension(CreateExtension): + # Available for django 1.10+ + # For previous versions use + # migrations.RunSQL('CREATE EXTENSION IF NOT EXISTS hll;', reverse_sql='DROP EXTENSION hll;') + def __init__(self): + self.name = 'hll' diff --git a/src/django_pg_hll/values.py b/src/django_pg_hll/values.py index 224516d..a86e789 100644 --- a/src/django_pg_hll/values.py +++ b/src/django_pg_hll/values.py @@ -1,11 +1,11 @@ -from collections import Iterable from copy import deepcopy from typing import Any -import six from abc import abstractmethod, ABCMeta from django.db.models.expressions import CombinedExpression, F, Func, Value +from .compatibility import string_types, Iterable + class HllJoinMixin: CONCAT = '||' @@ -27,7 +27,7 @@ class HllCombinedExpression(HllJoinMixin, CombinedExpression): pass -class HllFromHex(six.with_metaclass(ABCMeta, Func)): +class HllFromHex(Func, metaclass=ABCMeta): """ Constructs hll that can be saved from binary data (or it's psycopg representation) """ @@ -35,7 +35,7 @@ def __init__(self, data, *args, **extra): db_type = extra.pop('db_type', 'hll') # Psycopg2 returns Binary results as hex string, prefixed by \x but requires bytes for saving. - if isinstance(data, six.string_types) and data.startswith(r'\x'): + if isinstance(data, string_types) and data.startswith(r'\x'): data = bytearray.fromhex(data[2:]) elif isinstance(data, bytes): pass @@ -47,7 +47,7 @@ def __init__(self, data, *args, **extra): super(HllFromHex, self).__init__(Value(data), *args, **extra) -class HllValue(six.with_metaclass(ABCMeta, HllJoinMixin, Func)): +class HllValue(HllJoinMixin, Func, metaclass=ABCMeta): pass @@ -93,7 +93,7 @@ def parse_data(cls, data): # type: (Any) -> HllDataValue raise ValueError('No appropriate class found for value of type: %s' % str(type(data))) -class HllPrimitiveValue(six.with_metaclass(ABCMeta, HllDataValue)): +class HllPrimitiveValue(HllDataValue, metaclass=ABCMeta): def __init__(self, data, **extra): # type: (Any, **dict) -> None """ :param data: Data to build value from @@ -158,7 +158,7 @@ class HllText(HllPrimitiveValue): @classmethod def check(cls, data): - return isinstance(data, six.string_types) + return isinstance(data, string_types) class HllAny(HllPrimitiveValue): diff --git a/tests/compatibility.py b/tests/compatibility.py new file mode 100644 index 0000000..920fe35 --- /dev/null +++ b/tests/compatibility.py @@ -0,0 +1,20 @@ +from typing import Union + +import django + + +def psycopg_binary_to_bytes(data): # type: (Union[bytes, 'Binary']) -> bytes + """ + Since django 3.2 psycopg2.extensions.Binary is returned for BinaryField instead of raw byte string + :param data: Data to convert to bytes + :return: + """ + if django.VERSION < (3, 2): + return data + + from psycopg2.extensions import Binary + if not isinstance(data, Binary): + return data + + return data.adapted + diff --git a/tests/migrations/0001_initial.py b/tests/migrations/0001_initial.py index 393fbaa..a045ea5 100644 --- a/tests/migrations/0001_initial.py +++ b/tests/migrations/0001_initial.py @@ -1,6 +1,7 @@ from django.db import models, migrations from django_pg_hll import HllField +from django_pg_hll.migration import HllExtension class Migration(migrations.Migration): @@ -8,7 +9,7 @@ class Migration(migrations.Migration): dependencies = [] operations = [ - migrations.RunSQL('CREATE EXTENSION hll;', reverse_sql='DROP EXTENSION hll;'), + HllExtension(), migrations.CreateModel( name='FkModel', fields=[ diff --git a/tests/settings.py b/tests/settings.py index 344a801..ac1d111 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,15 +1,17 @@ """ This file contains django settings to run tests with runtests.py """ +import os + SECRET_KEY = 'fake-key' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'test', - 'USER': 'test', - 'PASSWORD': 'test', - 'HOST': '127.0.0.1', + 'USER': os.environ.get('PGUSER', 'test'), + 'PASSWORD': os.environ.get('PGPASS', 'test'), + 'HOST': os.environ.get('PGHOST', '127.0.0.1'), 'PORT': '5432' } } diff --git a/tests/test_hll_field.py b/tests/test_hll_field.py index 81a41aa..962af2c 100644 --- a/tests/test_hll_field.py +++ b/tests/test_hll_field.py @@ -1,13 +1,12 @@ from unittest import skipIf -import six from django.db import connection from django.db.models import F from django.test import TestCase from django_pg_hll.aggregate import Cardinality, UnionAgg, UnionAggCardinality, CardinalitySum, HllSchemaVersion, \ HllType, HllLog2M, HllRegWidth, HllExpThreshold, HllSParseOn -from django_pg_hll.compatibility import django_pg_bulk_update_available +from django_pg_hll.compatibility import django_pg_bulk_update_available, string_types from django_pg_hll.fields import HllField from django_pg_hll.values import HllEmpty, HllInteger @@ -81,7 +80,7 @@ def test_hex_convertion(self): instance.refresh_from_db() - self.assertIsInstance(instance.hll_field, six.string_types) + self.assertIsInstance(instance.hll_field, string_types) self.assertEqual(instance.hll_field[:2], r'\x') instance.save() diff --git a/tests/test_values.py b/tests/test_values.py index 5c663a6..0e3c693 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -4,6 +4,7 @@ from django.db.models.sql import Query from django_pg_hll import HllEmpty, HllSmallInt, HllInteger, HllBigint, HllBoolean, HllByteA, HllText, HllAny, HllSet +from tests.compatibility import psycopg_binary_to_bytes from tests.models import TestModel @@ -149,7 +150,7 @@ def test_sql(self): val = HllByteA(b'abc') sql, params = val.as_sql(self.compiler, connection) self.assertEqual('hll_empty() || hll_hash_bytea(%s)', sql) - self.assertListEqual([b'abc'], params) + self.assertListEqual([b'abc'], [psycopg_binary_to_bytes(param) for param in params]) def test_check(self): # Check correct values