diff --git a/.github/workflows/install-postgres.sh b/.github/workflows/install-postgres.sh index 70d42f60..c3f27186 100755 --- a/.github/workflows/install-postgres.sh +++ b/.github/workflows/install-postgres.sh @@ -38,7 +38,11 @@ elif [ "${ID}" = "centos" ]; then "postgresql${PGVERSION}-server" \ "postgresql${PGVERSION}-contrib" ln -s "/usr/pgsql-${PGVERSION}/bin/pg_config" "/usr/local/bin/pg_config" +elif [ "${ID}" = "alpine" ]; then + apk add shadow postgresql postgresql-dev postgresql-contrib else echo "install-postgres.sh: Unsupported distro: ${distro}" >&2 exit 1 fi + +useradd -m -s /bin/bash apgtest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e388e7bb..d1e2cfd5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,8 +74,13 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - cibw_python: ["cp37-*", "cp38-*", "cp39-*", "cp310-*"] - cibw_arch: ["auto64"] + cibw_python: ["cp36-*", "cp37-*", "cp38-*", "cp39-*", "cp310-*"] + cibw_arch: ["auto64", "auto32"] + exclude: + - os: macos-latest + cibw_arch: "auto32" + - os: ubuntu-latest + cibw_arch: "auto32" defaults: run: @@ -90,24 +95,11 @@ jobs: fetch-depth: 50 submodules: true - - uses: pypa/cibuildwheel@v2.1.1 + - uses: pypa/cibuildwheel@v2.2.2 env: CIBW_BUILD_VERBOSITY: 1 CIBW_BUILD: ${{ matrix.cibw_python }} CIBW_ARCHS: ${{ matrix.cibw_arch }} - CIBW_BEFORE_ALL_LINUX: > - yum -y install libffi-devel - && env PGVERSION=12 .github/workflows/install-postgres.sh - && useradd -m -s /bin/bash apgtest - CIBW_TEST_EXTRAS: "test" - CIBW_TEST_COMMAND: > - python {project}/tests/__init__.py - CIBW_TEST_COMMAND_WINDOWS: > - python {project}\tests\__init__.py - CIBW_TEST_COMMAND_LINUX: > - PY=`which python` - && chmod -R go+rX "$(dirname $(dirname $(dirname $PY)))" - && su -p -l apgtest -c "$PY {project}/tests/__init__.py" - uses: actions/upload-artifact@v2 with: diff --git a/Makefile b/Makefile index 9ad5d2e7..7a09181c 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ clean: rm -fr dist/ doc/_build/ rm -fr asyncpg/pgproto/*.c asyncpg/pgproto/*.html rm -fr asyncpg/pgproto/codecs/*.html + rm -fr asyncpg/pgproto/*.so rm -fr asyncpg/protocol/*.c asyncpg/protocol/*.html rm -fr asyncpg/protocol/*.so build *.egg-info rm -fr asyncpg/protocol/codecs/*.html diff --git a/asyncpg/_version.py b/asyncpg/_version.py index 2f106a98..bd1fc082 100644 --- a/asyncpg/_version.py +++ b/asyncpg/_version.py @@ -10,4 +10,4 @@ # supported platforms, publish the packages on PyPI, merge the PR # to the target branch, create a Git tag pointing to the commit. -__version__ = '0.24.0' +__version__ = '0.25.0' diff --git a/asyncpg/pgproto b/asyncpg/pgproto index 1720f8af..a4178145 160000 --- a/asyncpg/pgproto +++ b/asyncpg/pgproto @@ -1 +1 @@ -Subproject commit 1720f8af63725d79454884cfa787202a50eb5430 +Subproject commit a4178145cd7cc3a44eee20cfc9e8b94a7fed2053 diff --git a/asyncpg/protocol/codecs/range.pyx b/asyncpg/protocol/codecs/range.pyx index 4270d854..1038c18d 100644 --- a/asyncpg/protocol/codecs/range.pyx +++ b/asyncpg/protocol/codecs/range.pyx @@ -146,6 +146,8 @@ cdef multirange_encode(ConnectionSettings settings, WriteBuffer buf, encode_func_ex encoder, const void *encoder_arg): cdef: WriteBuffer elem_data + ssize_t elem_data_len + ssize_t elem_count if not isinstance(obj, SequenceABC): raise TypeError( @@ -157,10 +159,20 @@ cdef multirange_encode(ConnectionSettings settings, WriteBuffer buf, for elem in obj: range_encode(settings, elem_data, elem, elem_oid, encoder, encoder_arg) + elem_count = len(obj) + if elem_count > INT32_MAX: + raise OverflowError(f'too many elements in multirange value') + + elem_data_len = elem_data.len() + if elem_data_len > INT32_MAX - 4: + raise OverflowError( + f'size of encoded multirange datum exceeds the maximum allowed' + f' {INT32_MAX - 4} bytes') + # Datum length - buf.write_int32(4 + elem_data.len()) + buf.write_int32(4 + elem_data_len) # Number of elements in multirange - buf.write_int32(len(obj)) + buf.write_int32(elem_count) buf.write_buffer(elem_data) diff --git a/asyncpg/protocol/prepared_stmt.pyx b/asyncpg/protocol/prepared_stmt.pyx index 63466db8..b1f2a66d 100644 --- a/asyncpg/protocol/prepared_stmt.pyx +++ b/asyncpg/protocol/prepared_stmt.pyx @@ -151,7 +151,7 @@ cdef class PreparedStatementState: writer.write_int16(self.args_num) for idx in range(self.args_num): codec = (self.args_codecs[idx]) - writer.write_int16(codec.format) + writer.write_int16(codec.format) else: # All arguments are in binary format writer.write_int32(0x00010001) @@ -203,7 +203,7 @@ cdef class PreparedStatementState: writer.write_int16(self.cols_num) for idx in range(self.cols_num): codec = (self.rows_codecs[idx]) - writer.write_int16(codec.format) + writer.write_int16(codec.format) else: # All columns are in binary format writer.write_int32(0x00010001) diff --git a/asyncpg/protocol/protocol.pyx b/asyncpg/protocol/protocol.pyx index bb548962..3f512a81 100644 --- a/asyncpg/protocol/protocol.pyx +++ b/asyncpg/protocol/protocol.pyx @@ -38,7 +38,7 @@ from asyncpg.protocol cimport record from libc.stdint cimport int8_t, uint8_t, int16_t, uint16_t, \ int32_t, uint32_t, int64_t, uint64_t, \ - UINT32_MAX + INT32_MAX, UINT32_MAX from asyncpg.exceptions import _base as apg_exc_base from asyncpg import compat diff --git a/asyncpg/protocol/record/recordobj.c b/asyncpg/protocol/record/recordobj.c index e912782f..4bf34c8a 100644 --- a/asyncpg/protocol/record/recordobj.c +++ b/asyncpg/protocol/record/recordobj.c @@ -63,7 +63,7 @@ ApgRecord_New(PyTypeObject *type, PyObject *desc, Py_ssize_t size) return PyErr_NoMemory(); } o = (ApgRecordObject *)type->tp_alloc(type, size); - if (!_ApgObject_GC_IS_TRACKED(o)) { + if (!_ApgObject_GC_IS_TRACKED((PyObject *)o)) { PyErr_SetString( PyExc_TypeError, "record subclass is not tracked by GC" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..9e4ce7f9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,24 @@ +[project] +requires-python = ">=3.6" + +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.cibuildwheel] +build-frontend = "build" +test-extras = "test" + +[tool.cibuildwheel.macos] +test-command = "python {project}/tests/__init__.py" + +[tool.cibuildwheel.windows] +test-command = "python {project}\\tests\\__init__.py" + +[tool.cibuildwheel.linux] +before-all = ".github/workflows/install-postgres.sh" +test-command = """\ + PY=`which python` \ + && chmod -R go+rX "$(dirname $(dirname $(dirname $PY)))" \ + && su -l apgtest -c "$PY {project}/tests/__init__.py" \ + """ diff --git a/setup.py b/setup.py index 4da3fb25..332bad3f 100644 --- a/setup.py +++ b/setup.py @@ -274,9 +274,13 @@ def finalize_options(self): author_email='hello@magic.io', url='https://github.com/MagicStack/asyncpg', license='Apache License, Version 2.0', - packages=['asyncpg'], - provides=['asyncpg'], - include_package_data=True, + packages=setuptools.find_packages( + exclude=['tests', 'tools'], + ), + package_data={ + # Cython sources needed for tracebacks + "": ["*.pyx", "*.pxd", "*.pxi"], + }, ext_modules=[ setuptools.extension.Extension( "asyncpg.pgproto.pgproto", diff --git a/tests/__init__.py b/tests/__init__.py index 6282ebe5..c412aff7 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -18,6 +18,6 @@ def suite(): if __name__ == '__main__': - runner = unittest.runner.TextTestRunner() + runner = unittest.runner.TextTestRunner(verbosity=2) result = runner.run(suite()) sys.exit(not result.wasSuccessful()) diff --git a/tests/test_connect.py b/tests/test_connect.py index d66a087b..b78f4f48 100644 --- a/tests/test_connect.py +++ b/tests/test_connect.py @@ -1438,7 +1438,8 @@ async def test_executemany_uvloop_ssl_issue_700(self): await con.close() @unittest.skipIf( - sys.version_info < (3, 7), "Python < 3.7 doesn't have ssl.TLSVersion" + sys.version_info < (3, 7), + "Python < 3.7 doesn't have ssl.TLSVersion" ) async def test_tls_version(self): if self.cluster.get_pg_version() < (12, 0): @@ -1455,12 +1456,15 @@ async def test_tls_version(self): ) try: self.loop.set_exception_handler(lambda *args: None) - with self.assertRaisesRegex(ssl.SSLError, 'protocol version'): + with self.assertRaisesRegex( + ssl.SSLError, + '(protocol version)|(handshake failure)', + ): await self.connect( dsn='postgresql://ssl_user@localhost/postgres' '?sslmode=require&ssl_min_protocol_version=TLSv1.3' ) - with self.assertRaises(ssl.SSLError): + with self.assertRaises((ssl.SSLError, ConnectionResetError)): await self.connect( dsn='postgresql://ssl_user@localhost/postgres' '?sslmode=require'