Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make aiopg installable in PyPy environment #361

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ python:
- "3.4"
- "3.5"
- "3.6"
- "pypy3.5-5.8.0"

install:
- python setup.py install
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGES
-------

release-candidate
^^^^^^^^^^^^^^^^^

* Add PyPy support through `psycopg2cffi` #361


0.13.0 (2016-12-02)
^^^^^^^^^^^^^^^^^^^

Expand Down
3 changes: 2 additions & 1 deletion aiopg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys
from collections import namedtuple

from .utils import PY_IMPL
from .connection import connect, Connection, TIMEOUT as DEFAULT_TIMEOUT
from .cursor import Cursor
from .pool import create_pool, Pool
Expand Down Expand Up @@ -42,4 +43,4 @@ def _parse_version(ver):


# make pyflakes happy
(connect, create_pool, Connection, Cursor, Pool, DEFAULT_TIMEOUT)
(connect, create_pool, Connection, Cursor, Pool, DEFAULT_TIMEOUT, PY_IMPL)
6 changes: 3 additions & 3 deletions aiopg/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import weakref
import platform

import psycopg2
from psycopg2.extensions import (
from . import psycopg2_compat as psycopg2
from .psycopg2_compat.extensions import (
POLL_OK, POLL_READ, POLL_WRITE, POLL_ERROR)
from psycopg2 import extras
from .psycopg2_compat import extras

from .cursor import Cursor
from .utils import _ContextManager, PY_35, create_future
Expand Down
6 changes: 4 additions & 2 deletions aiopg/cursor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
import warnings

import psycopg2
from . import psycopg2_compat as psycopg2

from .log import logger
from .utils import PY_35, PY_352
Expand Down Expand Up @@ -338,7 +338,9 @@ def tzinfo_factory(self, val):
@asyncio.coroutine
def nextset(self):
# Not supported
self._impl.nextset() # raises psycopg2.NotSupportedError
# raises psycopg2.NotSupportedError on CPython
# raises NotImplementedError on PyPy
self._impl.nextset()

@asyncio.coroutine
def setoutputsize(self, size, column=None):
Expand Down
2 changes: 1 addition & 1 deletion aiopg/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import warnings


from psycopg2.extensions import TRANSACTION_STATUS_IDLE
from .psycopg2_compat.extensions import TRANSACTION_STATUS_IDLE

from .connection import connect, TIMEOUT
from .log import logger
Expand Down
7 changes: 7 additions & 0 deletions aiopg/psycopg2_compat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from ..utils import IS_PYPY


if IS_PYPY:
from psycopg2cffi import * # NOQA
else:
from psycopg2 import * # NOQA
7 changes: 7 additions & 0 deletions aiopg/psycopg2_compat/extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from ..utils import IS_PYPY


if IS_PYPY:
from psycopg2cffi.extensions import * # NOQA
else:
from psycopg2.extensions import * # NOQA
7 changes: 7 additions & 0 deletions aiopg/psycopg2_compat/extras.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from ..utils import IS_PYPY


if IS_PYPY:
from psycopg2cffi.extras import * # NOQA
else:
from psycopg2.extras import * # NOQA
9 changes: 7 additions & 2 deletions aiopg/sa/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
from .connection import SAConnection
from .exc import InvalidRequestError
from ..connection import TIMEOUT
from ..utils import PY_35, _PoolContextManager, _PoolAcquireContextManager
from ..utils import \
PY_35, _PoolContextManager, _PoolAcquireContextManager, IS_PYPY

try:
from sqlalchemy.dialects.postgresql.psycopg2 import PGDialect_psycopg2
if IS_PYPY:
from sqlalchemy.dialects.postgresql.psycopg2cffi import \
PGDialect_psycopg2cffi as PGDialect_psycopg2
else:
from sqlalchemy.dialects.postgresql.psycopg2 import PGDialect_psycopg2
from sqlalchemy.dialects.postgresql.psycopg2 import PGCompiler_psycopg2
except ImportError: # pragma: no cover
raise ImportError('aiopg.sa requires sqlalchemy')
Expand Down
3 changes: 3 additions & 0 deletions aiopg/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import asyncio
import platform
import sys


PY_35 = sys.version_info >= (3, 5)
PY_352 = sys.version_info >= (3, 5, 2)
PY_IMPL = platform.python_implementation()
IS_PYPY = (PY_IMPL == 'PyPy')

if PY_35:
from collections.abc import Coroutine
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ pytest-sugar==0.8.0
pytest-timeout==1.2.0
sphinxcontrib-asyncio==0.2.0
sqlalchemy==1.1.12
psycopg2==2.6.2
11 changes: 9 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import os
import platform
import re
import sys
from setuptools import setup


install_requires = ['psycopg2>=2.5.2']

PY_VER = sys.version_info
PY_IMPL = platform.python_implementation()

if PY_VER < (3, 4):
raise RuntimeError("aiopg doesn't suppport Python earlier than 3.4")


if PY_IMPL == 'PyPy':
install_requires = ['psycopg2cffi>=2.7.5']
else:
install_requires = ['psycopg2>=2.5.2']


def read(f):
return open(os.path.join(os.path.dirname(__file__), f)).read().strip()

Expand All @@ -35,6 +41,7 @@ def read_version():
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: Implementation :: PyPy',
'Operating System :: POSIX',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import contextlib
import gc
import logging
import psycopg2
import pytest
import re
import socket
Expand All @@ -15,6 +14,7 @@
from docker import Client as DockerClient

import aiopg
from aiopg import psycopg2_compat as psycopg2
from aiopg import sa


Expand Down
4 changes: 1 addition & 3 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import asyncio
import aiopg
import gc
import psycopg2
import psycopg2.extras
import psycopg2.extensions
import pytest
import socket
import time
import sys

from aiopg import psycopg2_compat as psycopg2
from aiopg.connection import Connection, TIMEOUT
from aiopg.cursor import Cursor
from aiopg.utils import ensure_future, create_future
Expand Down
11 changes: 8 additions & 3 deletions tests/test_cursor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import asyncio
import psycopg2
import psycopg2.tz
import pytest
import time

from aiopg import psycopg2_compat as psycopg2
from aiopg.utils import IS_PYPY
from aiopg.connection import TIMEOUT


Expand Down Expand Up @@ -249,9 +249,14 @@ def test_tzinfo_factory(connect):

@asyncio.coroutine
def test_nextset(connect):
if IS_PYPY:
exception_type = NotImplementedError
else:
exception_type = psycopg2.NotSupportedError

conn = yield from connect()
cur = yield from conn.cursor()
with pytest.raises(psycopg2.NotSupportedError):
with pytest.raises(exception_type):
yield from cur.nextset()


Expand Down
2 changes: 1 addition & 1 deletion tests/test_extended_types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
import uuid

from psycopg2.extras import Json
from aiopg.psycopg2_compat.extras import Json


@asyncio.coroutine
Expand Down
9 changes: 8 additions & 1 deletion tests/test_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from unittest import mock
import pytest
import sys
import gc

from psycopg2.extensions import TRANSACTION_STATUS_INTRANS
from aiopg.psycopg2_compat.extensions import TRANSACTION_STATUS_INTRANS

import aiopg
from aiopg.connection import Connection, TIMEOUT
from aiopg.pool import Pool
from aiopg.utils import IS_PYPY


@asyncio.coroutine
Expand Down Expand Up @@ -474,6 +476,11 @@ def test___del__(loop, pg_params, warning):
pool = yield from aiopg.create_pool(loop=loop, **pg_params)
with warning(ResourceWarning):
del pool
if IS_PYPY:
# PyPy's GC is not based on reference counting, and the objects
# are not freed instantly when they are no longer reachable.
# Therefore, we explicitly collect unreachable objects here.
gc.collect()


@asyncio.coroutine
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sa_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from sqlalchemy import MetaData, Table, Column, Integer, String
from sqlalchemy.schema import DropTable, CreateTable

import psycopg2
from aiopg import psycopg2_compat as psycopg2


meta = MetaData()
Expand Down
14 changes: 11 additions & 3 deletions tests/test_sa_engine.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
from aiopg.connection import TIMEOUT
from aiopg.utils import IS_PYPY

import pytest
sa = pytest.importorskip("aiopg.sa") # noqa
Expand Down Expand Up @@ -35,14 +36,21 @@ def test_name(engine):


def test_driver(engine):
assert 'psycopg2' == engine.driver
if IS_PYPY:
driver = 'psycopg2cffi'
else:
driver = 'psycopg2'
assert driver == engine.driver


def test_dsn(engine, pg_params):
pg_params['password'] = 'x' * len(pg_params['password'])
if not IS_PYPY:
pg_params['password'] = 'x' * len(pg_params['password'])

dsn = ('dbname={database} user={user} password={password} '
'host={host} port={port}').format_map(pg_params)
assert dsn == engine.dsn

assert sorted(dsn.split()) == sorted(engine.dsn.split())


def test_minsize(engine):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sa_types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
from enum import Enum

import psycopg2
from aiopg import psycopg2_compat as psycopg2
import pytest
sa = pytest.importorskip("aiopg.sa") # noqa

Expand Down