Skip to content

Commit 0961ab6

Browse files
committed
Implemented connection pool-based DatabaseManager; Implement _get_cursor() contextmanager; Added system files
1 parent 7cd96f2 commit 0961ab6

File tree

8 files changed

+146
-39
lines changed

8 files changed

+146
-39
lines changed

.bumpversion.cfg

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[bumpversion]
2+
current_version = 0.1.0
3+
tag = True
4+
tag_name = {new_version}
5+
commit = True
6+
7+
[bumpversion:file:setup.py]
8+
9+
[bumpversion:file:pgclient/__init__.py]
10+

Makefile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# System variables
2+
ENV_DIR=$(CURDIR)/.env
3+
PYTHON=$(ENV_DIR)/bin/python
4+
COVERAGE=$(ENV_DIR)/bin/coverage
5+
PROJECT_NAME=pgclient
6+
7+
help:
8+
# target: help - Display callable targets
9+
@grep -e "^# target:" [Mm]akefile | sed -e 's/^# target: //g'
10+
11+
.PHONY: env
12+
env:
13+
# target: env - create virtualenv and install packages
14+
@virtualenv $(ENV_DIR)
15+
@$(ENV_DIR)/bin/pip install -r $(CURDIR)/requirements.txt
16+
17+
.PHONY: pypi_upload
18+
pypi_upload:
19+
# target: pypi_upload - Upload package to pypi.python.org
20+
@$(PYTHON) setup.py sdist upload
21+
22+
.PHONY: test
23+
test: env
24+
# target: test - Run tests
25+
@$(PYTHON) -m unittest discover
26+
27+
28+
.PHONY: test_ci
29+
test_ci: env
30+
# target: test_ci - Run tests command adapt for CI systems
31+
@$(PYTHON) -m unittest discover
32+
33+
.PHONY: test_coverage
34+
# target: test_coverage - Run tests with coverage
35+
test_coverage: env
36+
@$(COVERAGE) run --source=$(PROJECT_NAME) $(PYTHON) -m unittest discover

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
pgclient
2+
=============
3+
4+
pgclient - yet another pool-based psycopg2 wrapper.
5+
6+
7+
Quick start
8+
===========
9+
10+
11+
12+
13+
Installation
14+
============
15+
System dependencies
16+
-------------------
17+
18+
Install the following dependencies via `.deb` or `.rpm` packages
19+
20+
* python-dev
21+
* libpq-dev
22+
23+
Install package
24+
---------------
25+
26+
pip install pgclient
27+
28+
29+
Bug tracker
30+
===========
31+
32+
Warm welcome to suggestions and concerns
33+
34+
https://github.com/prawn-cake/pgclient/issues
35+
36+
37+
License
38+
=======
39+
40+
MIT - http://opensource.org/licenses/MIT

pgclient/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
# -*- coding: utf-8 -*-
22

3-
4-
if __name__ == '__main__':
5-
pass
3+
__version__ = '0.1.0'

pgclient/client.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22
import psycopg2
3+
import psycopg2.pool as pgpool
34
import psycopg2.extras as pg_extras
45
import abc
56
import six
@@ -11,51 +12,51 @@
1112

1213

1314
class DatabaseManager(object):
14-
def __init__(self, dsn, pool_size=1):
15+
def __init__(self, dsn=None, database=None, user=None, password=None,
16+
host=None, port=None, pool_size=1):
1517
self.dsn = dsn
16-
self._conn = None
17-
self.pool_size = pool_size
18+
19+
# Pass connection params as is
20+
conn_params = dict(dsn=dsn, database=database, user=user,
21+
password=password, host=host, port=port)
22+
if pool_size < 1:
23+
raise ValueError('Wrong pool_size value. Must be >= 1. '
24+
'Current: {}'.format(pool_size))
25+
# Init thread-safe connection pool
26+
self.pool = pgpool.ThreadedConnectionPool(
27+
minconn=1, maxconn=pool_size, **conn_params)
1828

1929
@property
2030
def connection(self):
2131
"""Lazy connection property
2232
2333
:return: postgresql connection instance
2434
"""
25-
if self._conn is None:
26-
self._conn = self._get_connection()
27-
return self._conn
35+
return self.pool.getconn()
2836

37+
@contextmanager
38+
def _get_cursor(self, cursor_factory=None):
39+
conn = self.connection
40+
try:
41+
yield conn.cursor(cursor_factory=cursor_factory)
42+
conn.commit()
43+
except psycopg2.DatabaseError as err:
44+
conn.rollback()
45+
raise psycopg2.DatabaseError(err)
46+
finally:
47+
self.pool.putconn(conn)
48+
49+
# TODO: rename it
2950
@property
3051
def cursor(self):
31-
return self.connection.cursor()
52+
return self._get_cursor()
3253

3354
@property
3455
def dict_cursor(self):
3556
"""Return dict cursor. It enables accessing via column names instead
3657
of indexes
3758
"""
38-
return self.connection.cursor(cursor_factory=pg_extras.DictCursor)
39-
40-
def _get_connection(self):
41-
return psycopg2.connect(dsn=self.dsn)
42-
43-
@contextmanager
44-
def get_transaction_cursor(self):
45-
"""Public transaction context manager. Useful for getting transactional
46-
cursor to be able execute several SQL requests in one transaction
47-
48-
Example:
49-
with db_manager.get_transaction_cursor() as t_cursor:
50-
t_cursor.execute('SELECT * FROM ...')
51-
t_cursor.execute('INSERT INTO my_table VALUES ...')
52-
53-
So, the code above will be executed within one transaction
54-
55-
"""
56-
with self.connection as conn:
57-
with conn.cursor() as t_cursor:
58-
yield t_cursor
59+
return self._get_cursor(cursor_factory=pg_extras.DictCursor)
5960

6061

6162
@six.add_metaclass(abc.ABCMeta)

pgclient/tests.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
# -*- coding: utf-8 -*-
22
import unittest
3+
from pgclient.client import DatabaseManager
34

45

56
class DatabaseManagerTest(unittest.TestCase):
67
def setUp(self):
7-
pass
8+
self.db_manager = DatabaseManager(dsn='localhost')
89

910
def tearDown(self):
1011
pass
1112

1213
def test_connection(self):
13-
pass
14+
conn = self.db_manager.connection
15+
self.assertTrue(conn)
1416

1517
def test_cursor(self):
16-
pass
18+
with self.db_manager.cursor as cursor:
19+
result = cursor.execute('SELECT * FROM').fetchall()
20+
self.assertTrue(result)
1721

1822
def test_dict_cursor(self):
19-
pass
20-
21-
def test_get_transaction_cursor_context_manager(self):
22-
pass
23+
with self.db_manager.dict_cursor as cursor:
24+
result = cursor.execute('SELECT * FROM').fetchall()
25+
self.assertTrue(result)

requirements.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
psycopg2==2.6.1
2-
six
2+
3+
# Python 2 to 3 compatibility
4+
six==1.9.0
5+
6+
# System packages
7+
coverage==3.7.1
8+
bumpversion==0.5.3
9+
mock

setup.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from distutils.core import setup
2+
3+
setup(
4+
name='pgclient',
5+
version='0.1.0',
6+
packages=['pgclient'],
7+
url='https://github.com/prawn-cake/pgclient',
8+
license='MIT',
9+
author='Maksim Ekimovskii',
10+
author_email='[email protected]',
11+
description='Yet another psycopg2 wrapper'
12+
)

0 commit comments

Comments
 (0)