Merge branch 'packages'

This commit is contained in:
Daniele Varrazzo 2021-05-26 17:29:03 +01:00
commit 06c3c3a557
13 changed files with 528 additions and 54 deletions

88
.appveyor/packages.yml Normal file
View File

@ -0,0 +1,88 @@
version : 2.x.{build}
clone_folder: C:\Project
# We use the configuration to specify the package name
configuration:
- psycopg2
- psycopg2-binary
environment:
matrix:
# For Python versions available on Appveyor, see
# https://www.appveyor.com/docs/windows-images-software/#python
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019, PY_VER: "39", PY_ARCH: "32"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019, PY_VER: "39", PY_ARCH: "64"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "38", PY_ARCH: "32"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "38", PY_ARCH: "64"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "37", PY_ARCH: "32"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "37", PY_ARCH: "64"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "36", PY_ARCH: "32"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "36", PY_ARCH: "64"}
WORKFLOW: packages
OPENSSL_VERSION: "1_1_1h"
POSTGRES_VERSION: "13_0"
PSYCOPG2_TESTDB: psycopg2_test
PSYCOPG2_TESTDB_USER: postgres
PSYCOPG2_TESTDB_HOST: localhost
PGUSER: postgres
PGPASSWORD: Password12!
PGSSLMODE: require
# Add CWD to perl library path for PostgreSQL build on VS2019
PERL5LIB: .
# Select according to the service enabled
POSTGRES_DIR: C:\Program Files\PostgreSQL\9.6\
# The python used in the build process, not the one packages are built for
PYEXE: C:\Python36\python.exe
matrix:
fast_finish: false
services:
# Note: if you change this service also change POSTGRES_DIR
- postgresql96
cache:
# Rebuild cache if following file changes
# (See the file to zap the cache manually)
- C:\Others -> scripts\appveyor.cache_rebuild
# Script called before repo cloning
# init:
# Repository gets cloned, Cache is restored
install:
- "%PYEXE% scripts\\appveyor.py install"
# PostgreSQL server starts now
build: off
build_script:
- "%PYEXE% scripts\\appveyor.py build_script"
after_build:
- "%PYEXE% scripts\\appveyor.py after_build"
before_test:
- "%PYEXE% scripts\\appveyor.py before_test"
test_script:
- "%PYEXE% scripts\\appveyor.py test_script"
artifacts:
- path: dist\psycopg2-*\*.whl
name: wheel
- path: dist\psycopg2-*\*.exe
name: exe
# vim: set ts=4 sts=4 sw=4:

View File

@ -3,11 +3,6 @@ version : 2.x.{build}
clone_folder: C:\Project
environment:
global:
# MSVC Express 2008's setenv.cmd failes if /E:ON and /V:ON are not
# enabled in the batch script interpreter
CMD_IN_ENV: cmd /E:ON /V:ON /C .\appveyor\run_with_env.cmd
matrix:
# For Python versions available on Appveyor, see
# https://www.appveyor.com/docs/windows-images-software/#python
@ -20,6 +15,8 @@ environment:
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "36", PY_ARCH: "32"}
- {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "36", PY_ARCH: "64"}
WORKFLOW: tests
OPENSSL_VERSION: "1_1_1h"
POSTGRES_VERSION: "13_0"

View File

@ -50,3 +50,90 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5
build-manylinux_2_24:
strategy:
fail-fast: false
matrix:
include:
- platform: manylinux_2_24_x86_64
- platform: manylinux_2_24_i686
- platform: manylinux_2_24_aarch64
- platform: manylinux_2_24_ppc64le
runs-on: ubuntu-20.04
steps:
- name: Checkout repos
uses: actions/checkout@v2
- name: Set up QEMU for multi-arch build
uses: docker/setup-qemu-action@v1
- name: Build packages
run: >-
docker run --rm
-e PLAT=${{ matrix.platform }}
-e PACKAGE_NAME=psycopg2-binary
-e PYVERS="cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310"
-e PSYCOPG2_TESTDB=postgres
-e PSYCOPG2_TESTDB_HOST=172.17.0.1
-e PSYCOPG2_TESTDB_USER=postgres
-e PSYCOPG2_TESTDB_PASSWORD=password
-e PSYCOPG2_TEST_FAST=1
-v `pwd`:/src
--workdir /src
quay.io/pypa/${{ matrix.platform }}
./scripts/build/build_manylinux_2_24.sh
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: packages_${{ matrix.platform }}
path: |
dist/*/*${{ matrix.platform }}.whl
services:
postgresql:
image: postgres:13
env:
POSTGRES_PASSWORD: password
ports:
- 5432:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
build-macos:
runs-on: macos-10.15
strategy:
fail-fast: false
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9']
steps:
- name: Checkout repos
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Build packages
run: ./scripts/build/build_macos.sh
env:
PACKAGE_NAME: psycopg2-binary
PSYCOPG2_TESTDB: postgres
PSYCOPG2_TEST_FAST: 1
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: packages_macos
path: |
dist/*/*${{ matrix.platform }}.whl

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ env?
.vscode/
/rel
/wheels
/packages

9
NEWS
View File

@ -4,7 +4,6 @@ Current release
What's new in psycopg 2.9
-------------------------
- Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`).
- ``with connection`` starts a transaction on autocommit transactions too
(:ticket:`#941`).
- Escape table and column names in `~cursor.copy_from()` and
@ -12,6 +11,14 @@ What's new in psycopg 2.9
- Connection exceptions with sqlstate ``08XXX`` reclassified as
`~psycopg2.OperationalError` (a subclass of the previously used
`~psycopg2.DatabaseError`) (:ticket:`#1148`).
- Include library dirs required from libpq to work around MacOS build problems
(:ticket:`#1200`).
Other changes:
- Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`).
- Build system for Linux/MacOS binary packages moved to GitHub action, now
providing :pep:`600`\-style wheels packages.
What's new in psycopg 2.8.7

View File

@ -15,24 +15,12 @@ How to make a psycopg2 release
$ export VERSION=2.8.4
- In the `Travis settings`__ you may want to be sure that the variables
``TEST_PAST`` and ``TEST_FUTURE`` are set to 1 to check all
the supported postgres version.
- Push psycopg2 to master or to the maint branch. Make sure tests on `GitHub
Actions`__ and AppVeyor__ pass.
.. __: https://travis-ci.org/psycopg/psycopg2/settings
- Push psycopg2 to master or to the maint branch. Make sure tests on Travis__
and AppVeyor__ pass.
.. __: https://travis-ci.org/psycopg/psycopg2
.. __: https://github.com/psycopg/psycopg2/actions/workflows/tests.yml
.. __: https://ci.appveyor.com/project/psycopg/psycopg2
- For an extra test merge or rebase the `test_i686`__ branch on the commit to
release and push it too: this will test with Python 32 bits and debug
versions.
.. __: https://github.com/psycopg/psycopg2/tree/test_i686
- Create a signed tag with the content of the relevant NEWS bit and push it.
E.g.::
@ -49,34 +37,31 @@ How to make a psycopg2 release
- Fixed bug blah (:ticket:`#42`).
...
- Update the `psycopg2-wheels`_ submodule to the tag version and push. This
will build the packages on `Travis CI`__ and `AppVeyor`__ and upload them to
https://upload.psycopg.org/.
- Create the packages:
.. _psycopg2-wheels: https://github.com/psycopg/psycopg2-wheels
.. __: https://travis-ci.org/psycopg/psycopg2-wheels
.. __: https://ci.appveyor.com/project/psycopg/psycopg2-wheels
- On GitHub Actions run manually a `package build workflow`__.
- Download the packages generated (this assumes ssh configured properly)::
- On Appveyor change the `build settings`__ and replace the custom
configuration file name from ``.appveyor/tests.yml`` to
``.appveyor/packages.yml`` (yeah, that sucks a bit. Remember to put it
back to testing).
$ rsync -arv psycopg-upload:psycopg2-${VERSION} .
.. __: https://github.com/psycopg/psycopg2/actions/workflows/packages.yml
.. __: https://ci.appveyor.com/project/psycopg/psycopg2/settings
- Sign the packages and upload the signatures back::
$ for f in psycopg2-${VERSION}/*.{exe,tar.gz,whl}; do \
gpg --armor --detach-sign $f;
done
$ rsync -arv psycopg2-${VERSION} psycopg-upload:
- When the workflows have finished download the packages using the
``download_packages.py`` and ``download_packages_appveyor.py`` scripts from
the ``scripts/build`` directory. They will be saved in a
``psycopg2-${VERSION}`` directory.
- Remove the ``.exe`` from the dir, because we don't want to upload them on
PyPI::
$ rm -v psycopg2-${VERSION}/*.exe{,.asc}
$ rm -v psycopg2-${VERSION}/*.exe
- Only for stable packages: upload the packages and signatures on PyPI::
- Only for stable packages: upload the signed packages on PyPI::
$ twine upload psycopg2-${VERSION}/*
$ twine upload -s psycopg2-${VERSION}/*
- Create a release and release notes in the psycopg website, announce to
psycopg and pgsql-announce mailing lists.
@ -89,7 +74,7 @@ Releasing test packages
Test packages may be uploaded on the `PyPI testing site`__ using::
$ twine upload -r testpypi psycopg2-${VERSION}/*
$ twine upload -s -r testpypi psycopg2-${VERSION}/*
assuming `proper configuration`__ of ``~/.pypirc``.

View File

@ -699,13 +699,8 @@ class Options:
@property
def is_wheel(self):
"""Are we building the wheel packages or just the extension?"""
project_name = os.environ['APPVEYOR_PROJECT_NAME']
if project_name == 'psycopg2':
return False
elif project_name == 'psycopg2-wheels':
return True
else:
raise Exception(f"unexpected project name: {project_name}")
workflow = os.environ["WORKFLOW"]
return workflow == "packages"
@property
def py_dir(self):
@ -801,12 +796,7 @@ class Options:
@property
def package_dir(self):
"""
The directory containing the psycopg code checkout dir.
Building psycopg it is clone_dir, building the wheels it is a submodule.
"""
return self.clone_dir / 'psycopg2' if self.is_wheel else self.clone_dir
return self.clone_dir
@property
def dist_dir(self):

77
scripts/build/build_macos.sh Executable file
View File

@ -0,0 +1,77 @@
#!/bin/bash
# Create macOS wheels for psycopg2
#
# Following instructions from https://github.com/MacPython/wiki/wiki/Spinning-wheels
# Cargoculting pieces of implementation from https://github.com/matthew-brett/multibuild
set -euo pipefail
set -x
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PRJDIR="$( cd "${DIR}/../.." && pwd )"
brew install gnu-sed postgresql@13
# Start the database for testing
brew services start postgresql
for i in $(seq 10 -1 0); do
eval pg_isready && break
if [ $i == 0 ]; then
echo "PostgreSQL service not ready, giving up"
exit 1
fi
echo "PostgreSQL service not ready, waiting a bit, attempts left: $i"
sleep 5
done
# Find psycopg version
VERSION=$(grep -e ^PSYCOPG_VERSION "${PRJDIR}/setup.py" | gsed "s/.*'\(.*\)'/\1/")
# A gratuitous comment to fix broken vim syntax file: '")
DISTDIR="${PRJDIR}/dist/psycopg2-$VERSION"
mkdir -p "$DISTDIR"
# Install required python packages
pip install -U pip wheel delocate
# Replace the package name
gsed -i "s/^setup(name=\"psycopg2\"/setup(name=\"${PACKAGE_NAME}\"/" \
"${PRJDIR}/setup.py"
# Build the wheels
WHEELDIR="${PRJDIR}/wheels"
pip wheel -w ${WHEELDIR} .
delocate-listdeps ${WHEELDIR}/*.whl
# Check where is the libpq. I'm gonna kill it for testing
if [[ -z "${LIBPQ:-}" ]]; then
export LIBPQ=$(delocate-listdeps ${WHEELDIR}/*.whl | grep libpq)
fi
delocate-wheel ${WHEELDIR}/*.whl
# https://github.com/MacPython/wiki/wiki/Spinning-wheels#question-will-pip-give-me-a-broken-wheel
delocate-addplat --rm-orig -x 10_9 -x 10_10 ${WHEELDIR}/*.whl
cp ${WHEELDIR}/*.whl ${DISTDIR}
# kill the libpq to make sure tests don't depend on it
mv "$LIBPQ" "${LIBPQ}-bye"
# Install and test the built wheel
pip install ${PACKAGE_NAME} --no-index -f "$DISTDIR"
# Print psycopg and libpq versions
python -c "import psycopg2; print(psycopg2.__version__)"
python -c "import psycopg2; print(psycopg2.__libpq_version__)"
python -c "import psycopg2; print(psycopg2.extensions.libpq_version())"
# fail if we are not using the expected libpq library
# Disabled as we just use what's available on the system on macOS
# if [[ "${WANT_LIBPQ:-}" ]]; then
# python -c "import psycopg2, sys; sys.exit(${WANT_LIBPQ} != psycopg2.extensions.libpq_version())"
# fi
python -c "import tests; tests.unittest.main(defaultTest='tests.test_suite')"
# just because I'm a boy scout
mv "${LIBPQ}-bye" "$LIBPQ"

View File

@ -0,0 +1,75 @@
#!/bin/bash
# Create manylinux_2_24 wheels for psycopg2
#
# Look at the .github/workflows/packages.yml file for hints about how to use it.
set -euo pipefail
set -x
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PRJDIR="$( cd "${DIR}/../.." && pwd )"
# Build all the available versions, or just the ones specified in PYVERS
if [ ! "${PYVERS:-}" ]; then
PYVERS="$(ls /opt/python/)"
fi
# Find psycopg version
VERSION=$(grep -e ^PSYCOPG_VERSION "${PRJDIR}/setup.py" | sed "s/.*'\(.*\)'/\1/")
# A gratuitous comment to fix broken vim syntax file: '")
DISTDIR="${PRJDIR}/dist/psycopg2-$VERSION"
# Replace the package name
if [[ "${PACKAGE_NAME:-}" ]]; then
sed -i "s/^setup(name=\"psycopg2\"/setup(name=\"${PACKAGE_NAME}\"/" \
"${PRJDIR}/setup.py"
fi
# Install prerequisite libraries
curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt stretch-pgdg main" \
> /etc/apt/sources.list.d/pgdg.list
apt-get -y update
apt-get install -y libpq-dev
# Create the wheel packages
for PYVER in $PYVERS; do
PYBIN="/opt/python/${PYVER}/bin"
"${PYBIN}/pip" wheel "${PRJDIR}" -w "${PRJDIR}/dist/"
done
# Bundle external shared libraries into the wheels
for WHL in "${PRJDIR}"/dist/*.whl; do
auditwheel repair "$WHL" -w "$DISTDIR"
done
# Make sure the libpq is not in the system
for f in $(find /usr/lib /usr/lib64 -name libpq\*) ; do
mkdir -pv "/libpqbak/$(dirname $f)"
mv -v "$f" "/libpqbak/$(dirname $f)"
done
# Install packages and test
cd "${PRJDIR}"
for PYVER in $PYVERS; do
PYBIN="/opt/python/${PYVER}/bin"
"${PYBIN}/pip" install ${PACKAGE_NAME} --no-index -f "$DISTDIR"
# Print psycopg and libpq versions
"${PYBIN}/python" -c "import psycopg2; print(psycopg2.__version__)"
"${PYBIN}/python" -c "import psycopg2; print(psycopg2.__libpq_version__)"
"${PYBIN}/python" -c "import psycopg2; print(psycopg2.extensions.libpq_version())"
# Fail if we are not using the expected libpq library
if [[ "${WANT_LIBPQ:-}" ]]; then
"${PYBIN}/python" -c "import psycopg2, sys; sys.exit(${WANT_LIBPQ} != psycopg2.extensions.libpq_version())"
fi
"${PYBIN}/python" -c "import tests; tests.unittest.main(defaultTest='tests.test_suite')"
done
# Restore the libpq packages
for f in $(cd /libpqbak/ && find . -not -type d); do
mv -v "/libpqbak/$f" "/$f"
done

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
"""Download packages from github actions artifacts
"""
import io
import os
import sys
import logging
from pathlib import Path
from zipfile import ZipFile
import requests
logger = logging.getLogger()
logging.basicConfig(
level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s"
)
REPOS = "psycopg/psycopg2"
WORKFLOW_NAME = "Build packages"
class ScriptError(Exception):
"""Controlled exception raised by the script."""
def main():
try:
token = os.environ["GITHUB_TOKEN"]
except KeyError:
raise ScriptError("please set a GITHUB_TOKEN to download artifacts")
s = requests.Session()
s.headers["Accept"] = "application/vnd.github.v3+json"
s.headers["Authorization"] = f"token {token}"
logger.info("looking for recent runs")
resp = s.get(f"https://api.github.com/repos/{REPOS}/actions/runs?per_page=10")
resp.raise_for_status()
for run in resp.json()["workflow_runs"]:
if run["name"] == WORKFLOW_NAME:
break
else:
raise ScriptError(f"couldn't find {WORKFLOW_NAME!r} in recent runs")
logger.info(f"looking for run {run['id']} artifacts")
resp = s.get(f"{run['url']}/artifacts")
resp.raise_for_status()
artifacts = resp.json()["artifacts"]
dest = Path("packages")
if not dest.exists():
logger.info(f"creating dir {dest}")
dest.mkdir()
for artifact in artifacts:
logger.info(f"downloading {artifact['name']} archive")
zip_url = artifact["archive_download_url"]
resp = s.get(zip_url)
with ZipFile(io.BytesIO(resp.content)) as zf:
logger.info("extracting archive content")
zf.extractall(dest)
logger.info(f"now you can run: 'twine upload -s {dest}/*'")
if __name__ == "__main__":
try:
sys.exit(main())
except ScriptError as e:
logger.error("%s", e)
sys.exit(1)
except KeyboardInterrupt:
logger.info("user interrupt")
sys.exit(1)

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
"""Download packages from github actions artifacts
"""
import os
import sys
import logging
from pathlib import Path
import requests
logger = logging.getLogger()
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
API_URL = "https://ci.appveyor.com/api"
REPOS = "psycopg/psycopg2"
WORKFLOW_NAME = "Build packages"
class ScriptError(Exception):
"""Controlled exception raised by the script."""
def main():
try:
token = os.environ["APPVEYOR_TOKEN"]
except KeyError:
raise ScriptError("please set a APPVEYOR_TOKEN to download artifacts")
s = requests.Session()
s.headers["Content-Type"] = "application/json"
s.headers["Authorization"] = f"Bearer {token}"
logger.info("fetching last run")
resp = s.get(f"{API_URL}/projects/{REPOS}/")
resp.raise_for_status()
data = resp.json()
jobs = data["build"]["jobs"]
for job in jobs:
if job["status"] != "success":
raise ScriptError("status for job {job['jobId']} is {job['status']}")
logger.info(f"fetching artifacts info for {job['name']}")
resp = s.get(f"{API_URL}/buildjobs/{job['jobId']}/artifacts/")
resp.raise_for_status()
afs = resp.json()
for af in afs:
fn = af["fileName"]
if fn.startswith("dist/"):
fn = fn.split("/", 1)[1]
dest = Path("packages") / fn
logger.info(f"downloading {dest}")
resp = s.get(
f"{API_URL}/buildjobs/{job['jobId']}/artifacts/{af['fileName']}"
)
resp.raise_for_status()
if not dest.parent.exists():
dest.parent.mkdir()
with dest.open("wb") as f:
f.write(resp.content)
logger.info("now you can run: 'twine upload -s packages/*'")
if __name__ == "__main__":
try:
sys.exit(main())
except ScriptError as e:
logger.error("%s", e)
sys.exit(1)
except KeyboardInterrupt:
logger.info("user interrupt")
sys.exit(1)

View File

@ -376,6 +376,16 @@ For further information please check the 'doc/src/install.rst' file (also at
self.library_dirs.append(pg_config_helper.query("libdir"))
self.include_dirs.append(pg_config_helper.query("includedir"))
self.include_dirs.append(pg_config_helper.query("includedir-server"))
# add includedirs from cppflags, libdirs from ldflags
for token in pg_config_helper.query("ldflags").split():
if token.startswith("-L"):
self.library_dirs.append(token[2:])
for token in pg_config_helper.query("cppflags").split():
if token.startswith("-I"):
self.include_dirs.append(token[2:])
pgversion = pg_config_helper.query("version").split()[1]
verre = re.compile(

View File

@ -178,7 +178,10 @@ class ConnectingTestCase(unittest.TestCase):
if libname is None and platform.system() == 'Windows':
raise self.skipTest("can't import libpq on windows")
rv = ConnectingTestCase._libpq = ctypes.pydll.LoadLibrary(libname)
try:
rv = ConnectingTestCase._libpq = ctypes.pydll.LoadLibrary(libname)
except OSError as e:
raise self.skipTest("couldn't open libpq for testing: %s" % e)
return rv