Skip to content

Commit a153a06

Browse files
committed
Replace connection property with acquire/release methods; Updated system_tests - coverage ~96%; Added .coveragerc; Remove helpless unittests, keep only system tests
1 parent 4a833f8 commit a153a06

File tree

5 files changed

+55
-90
lines changed

5 files changed

+55
-90
lines changed

.coveragerc

+2-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ exclude_lines =
99

1010
# Don't complain about missing debug-only code:
1111
def __repr__
12-
def __unicode__
1312
if self\.debug
1413

1514
# Don't complain if tests don't hit defensive assertion code:
@@ -23,9 +22,9 @@ exclude_lines =
2322
include =
2423
pgclient/*
2524
omit =
26-
pgclient/tests.py
25+
pgclient/system_test/*
2726

2827
ignore_errors = True
2928

3029
[html]
31-
directory = coverage_html_report
30+
directory = coverage_html_report

pgclient/client.py

+18-8
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,48 @@ def __init__(self, dsn=None, database=None, user=None, password=None,
1212
self.dsn = dsn
1313

1414
# Pass connection params as is
15-
conn_params = dict(dsn=dsn, database=database, user=user,
16-
password=password, host=host, port=port)
15+
conn_params = dict(dsn=dsn,
16+
database=database,
17+
user=user,
18+
password=password,
19+
host=host,
20+
port=port)
1721
if pool_size < 1:
1822
raise ValueError('Wrong pool_size value. Must be >= 1. '
1923
'Current: {}'.format(pool_size))
2024
# Init thread-safe connection pool
2125
self._pool = pgpool.ThreadedConnectionPool(
2226
minconn=1, maxconn=pool_size, **conn_params)
2327

24-
@property
25-
def _connection(self):
26-
"""Acquire pool connection property
28+
def acquire_conn(self):
29+
"""Get new pool connection
2730
28-
:return: postgresql connection instance
31+
:return: psycopg2 connection object
2932
"""
3033
return self._pool.getconn()
3134

35+
def release_conn(self, conn):
36+
"""Release connection to a pool
37+
38+
:param conn: psycopg2 connection object
39+
"""
40+
self._pool.putconn(conn)
41+
3242
@contextmanager
3343
def _get_cursor(self, cursor_factory=None):
3444
"""Get connection cursor context manager
3545
3646
:param cursor_factory: pg_extras.* cursor factory class
3747
"""
38-
conn = self._connection
48+
conn = self.acquire_conn()
3949
try:
4050
yield conn.cursor(cursor_factory=cursor_factory)
4151
conn.commit()
4252
except psycopg2.DatabaseError as err:
4353
conn.rollback()
4454
raise psycopg2.DatabaseError(err.message)
4555
finally:
46-
self._pool.putconn(conn)
56+
self.release_conn(conn)
4757

4858
@property
4959
def cursor(self):

pgclient/system_test/system_test.py

+35-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import os.path as op
77
import psycopg2
8+
from psycopg2.pool import PoolError
89

910
sys.path.append(
1011
op.abspath(op.dirname(__file__)) + '/../'
@@ -24,12 +25,14 @@ class PostgresClientSystemTest(unittest.TestCase):
2425
DB_NAME = 'test'
2526
DB_PORT = os.environ.get('POSTGRES_PORT', 5432)
2627
TABLE_NAME = 'users'
28+
POOL_SIZE = 3
2729

2830
def setUp(self):
29-
dsn = 'user={} password={} dbname={} host=localhost port={}'.format(
30-
self.DB_USER, self.DB_PASSWORD, self.DB_NAME, self.DB_PORT)
31+
self.dsn = 'user={} password={} dbname={} host=localhost port={}'\
32+
.format(self.DB_USER, self.DB_PASSWORD, self.DB_NAME, self.DB_PORT)
3133
try:
32-
self.pg_client = PostgresClient(dsn=dsn, pool_size=10)
34+
self.pg_client = PostgresClient(dsn=self.dsn,
35+
pool_size=self.POOL_SIZE)
3336
except psycopg2.OperationalError as err:
3437
print('Check that postgres docker container is started. '
3538
'Check README for more information')
@@ -65,6 +68,12 @@ def _drop_table(self):
6568
def tearDown(self):
6669
self._drop_table()
6770

71+
def test_create_with_wrong_pool_value(self):
72+
with self.assertRaises(ValueError) as err:
73+
pg_client = PostgresClient(dsn=self.dsn, pool_size=0)
74+
self.assertIsNone(pg_client)
75+
self.assertIn('Wrong pool_size value', err)
76+
6877
def test_cursor(self):
6978
with self.pg_client.cursor as cursor:
7079
cursor.execute('SELECT * FROM users')
@@ -98,10 +107,28 @@ def test_success_transaction(self):
98107
self.assertEqual(len(result_set), 101)
99108

100109
def test_rollback_transaction(self):
101-
# Insert null username must cause an error
102-
with self.pg_client.cursor as transaction:
103-
with self.assertRaises(psycopg2.DatabaseError) as err:
110+
# Inserting null username value must raise an error
111+
with self.assertRaises(psycopg2.DatabaseError) as err:
112+
with self.pg_client.cursor as transaction:
104113
transaction.execute(
105-
"INSERT INTO {} (username) VALUES (%s)".format(self.TABLE_NAME),
114+
"INSERT INTO {} (username) VALUES (%s)".format(
115+
self.TABLE_NAME),
106116
(None, ))
107-
self.assertIn('null value in column', err.exception.message)
117+
print('abc')
118+
self.assertIn('null value in column', err.exception.message)
119+
print('transaction finished')
120+
121+
def test_connection_pool_overflow(self):
122+
# Consume all connection to check overflow case
123+
connections = []
124+
for i in range(self.POOL_SIZE):
125+
connections.append(self.pg_client.acquire_conn())
126+
127+
with self.assertRaises(PoolError) as err:
128+
with self.pg_client.cursor as cursor:
129+
self.assertIsNone(cursor)
130+
self.assertIn('connection pool exhausted', err)
131+
132+
# Release all connections back to pool
133+
for conn in connections:
134+
self.pg_client.release_conn(conn)

pgclient/tests/__init__.py

-5
This file was deleted.

pgclient/tests/test_client.py

-66
This file was deleted.

0 commit comments

Comments
 (0)