Merge pull request #3 from sprockets/2.0-URI

Change to URI based env var config
This commit is contained in:
vrajmohan 2014-10-07 15:19:40 -04:00
commit 2e9749fae2
6 changed files with 43 additions and 94 deletions

View file

@ -26,8 +26,8 @@ Requirements
Example Example
------- -------
The following example sets the environment variables for connecting to The following example sets the environment variable for connecting to
PostgreSQL on localhost to the ``postgres`` database and issues a query. PostgreSQL on localhost to the ``production`` database and issues a query.
.. code:: python .. code:: python
@ -35,12 +35,9 @@ PostgreSQL on localhost to the ``postgres`` database and issues a query.
from sprockets.clients import postgresql from sprockets.clients import postgresql
os.environ['POSTGRES_HOST'] = 'localhost' os.environ['PGSQL_PROD'] = 'postgresql://postgres@localhost:5432/production'
os.environ['POSTGRES_USER'] = 'postgres'
os.environ['POSTGRES_PORT'] = 5432
os.environ['POSTGRES_DBNAME'] = 'postgres'
session = postgresql.Session('postgres') session = postgresql.Session('prod')
result = session.query('SELECT 1') result = session.query('SELECT 1')
print(repr(result)) print(repr(result))

View file

@ -9,12 +9,9 @@ PostgreSQL on localhost to the ``postgres`` database and issues a query.
from sprockets.clients import postgresql from sprockets.clients import postgresql
os.environ['POSTGRES_HOST'] = 'localhost' os.environ['PGSQL'] = 'postgresql://postgres@localhost:5432/postgres'
os.environ['POSTGRES_USER'] = 'postgres'
os.environ['POSTGRES_PORT'] = 5432
os.environ['POSTGRES_DBNAME'] = 'postgres'
session = postgresql.Session('postgres') session = postgresql.Session()
result = session.query('SELECT 1') result = session.query('SELECT 1')
print(repr(result)) print(repr(result))
@ -30,15 +27,12 @@ class in a Tornado :py:class:`RequestHandler <tornado.web.RequestHandler>`.
from sprockets.clients import postgresql from sprockets.clients import postgresql
from tornado import web from tornado import web
os.environ['POSTGRES_HOST'] = 'localhost' os.environ['PGSQL_FOO'] = 'postgresql://postgres@localhost:5432/foo'
os.environ['POSTGRES_USER'] = 'postgres'
os.environ['POSTGRES_PORT'] = 5432
os.environ['POSTGRES_DBNAME'] = 'postgres'
class RequestHandler(web.RequestHandler): class RequestHandler(web.RequestHandler):
def initialize(self): def initialize(self):
self.session = postgresql.TornadoSession('postgres') self.session = postgresql.TornadoSession('foo')
@gen.coroutine @gen.coroutine
def get(self, *args, **kwargs): def get(self, *args, **kwargs):

View file

@ -1,5 +1,7 @@
Version History Version History
--------------- ---------------
- 2.0.0 [2014-10-07]
- Change the environment variable format to be URI based instead of a variable per host, port, dbname, etc.
- 1.0.1 [2014-09-05] - 1.0.1 [2014-09-05]
- Expose psycopg2/queries exceptions, objects, etc from ``sprockets.queries.postgresql`` - Expose psycopg2/queries exceptions, objects, etc from ``sprockets.queries.postgresql``
- Add integration testing with PostgreSQL - Add integration testing with PostgreSQL

View file

@ -4,7 +4,7 @@ import setuptools
setuptools.setup( setuptools.setup(
name='sprockets.clients.postgresql', name='sprockets.clients.postgresql',
version='1.0.1', version='2.0.0',
description=('PostgreSQL client library wrapper providing environment ' description=('PostgreSQL client library wrapper providing environment '
'variable based configuration'), 'variable based configuration'),
long_description=codecs.open('README.rst', encoding='utf-8').read(), long_description=codecs.open('README.rst', encoding='utf-8').read(),

View file

@ -5,19 +5,18 @@ The Session classes wrap the Queries :py:class:`Session <queries.Session>` and
:py:class:`TornadoSession <queries.tornado_session.TornadoSession>` classes :py:class:`TornadoSession <queries.tornado_session.TornadoSession>` classes
providing environment variable based configuration. providing environment variable based configuration.
The environment variables should be set using the ``DBNAME_[VARIABLE]`` format Environment variables should be set using the ``PGSQL[_DBNAME]`` format
where ``[VARIABLE]`` is one of ``HOST``, ``PORT``, ``DBNAME``, ``USER``, and where the value is a PostgreSQL URI.
``PASSWORD``.
For example, given the environment variables: For PostgreSQL URI format, see:
http://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING
As example, given the environment variable:
.. code:: python .. code:: python
FOO_HOST = 'foodb' PGSQL_FOO = 'postgresql://bar:baz@foohost:6000/foo'
FOO_PORT = '6000'
FOO_DBNAME = 'foo'
FOO_USER = 'bar'
FOO_PASSWORD = 'baz'
and code for creating a :py:class:`Session` instance for the database name and code for creating a :py:class:`Session` instance for the database name
``foo``: ``foo``:
@ -26,11 +25,12 @@ and code for creating a :py:class:`Session` instance for the database name
session = sprockets.postgresql.Session('foo') session = sprockets.postgresql.Session('foo')
The uri ``postgresql://bar:baz@foodb:6000/foo`` will be used when creating the A :py:class:`queries.Session` object will be created that connects to Postgres
instance of :py:class:`queries.Session`. running on ``foohost``, port ``6000`` using the username ``bar`` and the
password ``baz``, connecting to the ``foo`` database.
""" """
version_info = (1, 0, 1) version_info = (2, 0, 0)
__version__ = '.'.join(str(v) for v in version_info) __version__ = '.'.join(str(v) for v in version_info)
import logging import logging
@ -66,27 +66,21 @@ from queries import TransactionRollbackError
def _get_uri(dbname): def _get_uri(dbname):
"""Construct the URI for connecting to PostgreSQL by appending each """Return the URI for the specified database name from an environment
argument name to the dbname, delimited by an underscore and variable. If dbname is blank, the ``PGSQL`` environment variable is used,
capitalizing the new variable name. otherwise the database name is cast to upper case and concatenated to
``PGSQL_`` and the URI is retrieved from ``PGSQL_DBNAME``. For example,
Values will be retrieved from the environment variable and added to a if the value ``foo`` is passed in, the environment variable used would be
dictionary that is then passed in as keyword arguments to the ``PGSQL_FOO``.
:py:meth:`queries.uri` method to build the URI string.
:param str dbname: The database name to construct the URI for :param str dbname: The database name to construct the URI for
:return: str :return: str
:raises: KeyError
""" """
kwargs = dict() if not dbname:
for arg in _ARGUMENTS: return os.environ['PGSQL']
value = os.getenv(('%s_%s' % (dbname, arg)).upper()) return os.environ['PGSQL_{0}'.format(dbname).upper()]
if value:
if arg == 'port':
kwargs[arg] = int(value)
else:
kwargs[arg] = value
return queries.uri(**kwargs)
class Session(queries.Session): class Session(queries.Session):

View file

@ -10,7 +10,6 @@ try:
except ImportError: except ImportError:
import unittest import unittest
from tornado import gen
from sprockets.clients import postgresql from sprockets.clients import postgresql
import queries import queries
from tornado import testing from tornado import testing
@ -18,17 +17,9 @@ from tornado import testing
class TestGetURI(unittest.TestCase): class TestGetURI(unittest.TestCase):
def tearDown(self):
for key in ['HOST', 'PORT', 'DBNAME', 'USER', 'PASSWORD']:
del os.environ['TEST1_%s' % key]
def test_get_uri_returns_proper_values(self): def test_get_uri_returns_proper_values(self):
os.environ['PGSQL_TEST1'] = \
os.environ['TEST1_HOST'] = 'test1-host' 'postgresql://foo1:baz1@test1-host:5436/test1'
os.environ['TEST1_PORT'] = '5436'
os.environ['TEST1_DBNAME'] = 'test1'
os.environ['TEST1_USER'] = 'foo1'
os.environ['TEST1_PASSWORD'] = 'baz1'
self.assertEqual(postgresql._get_uri('test1'), self.assertEqual(postgresql._get_uri('test1'),
'postgresql://foo1:baz1@test1-host:5436/test1') 'postgresql://foo1:baz1@test1-host:5436/test1')
@ -39,17 +30,9 @@ class TestSession(unittest.TestCase):
@mock.patch('queries.session.Session.__init__') @mock.patch('queries.session.Session.__init__')
def setUp(self, mock_init): def setUp(self, mock_init):
self.mock_init = mock_init self.mock_init = mock_init
os.environ['TEST2_HOST'] = 'db1' os.environ['PGSQL_TEST2'] = 'postgresql://foo:baz@db1:5433/bar'
os.environ['TEST2_PORT'] = '5433'
os.environ['TEST2_DBNAME'] = 'bar'
os.environ['TEST2_USER'] = 'foo'
os.environ['TEST2_PASSWORD'] = 'baz'
self.session = postgresql.Session('test2') self.session = postgresql.Session('test2')
def tearDown(self):
for key in ['HOST', 'PORT', 'DBNAME', 'USER', 'PASSWORD']:
del os.environ['TEST2_%s' % key]
def test_session_invokes_queries_session(self): def test_session_invokes_queries_session(self):
self.assertTrue(self.mock_init.called) self.assertTrue(self.mock_init.called)
@ -59,17 +42,9 @@ class TestTornadoSession(unittest.TestCase):
@mock.patch('queries.tornado_session.TornadoSession.__init__') @mock.patch('queries.tornado_session.TornadoSession.__init__')
def setUp(self, mock_init): def setUp(self, mock_init):
self.mock_init = mock_init self.mock_init = mock_init
os.environ['TEST3_HOST'] = 'db1' os.environ['PGSQL_TEST3'] = 'postgresql://foo:baz@db1:5434/bar'
os.environ['TEST3_PORT'] = '5434'
os.environ['TEST3_DBNAME'] = 'bar'
os.environ['TEST3_USER'] = 'foo'
os.environ['TEST3_PASSWORD'] = 'baz'
self.session = postgresql.TornadoSession('test3') self.session = postgresql.TornadoSession('test3')
def tearDown(self):
for key in ['HOST', 'PORT', 'DBNAME', 'USER', 'PASSWORD']:
del os.environ['TEST3_%s' % key]
def test_session_invokes_queries_session(self): def test_session_invokes_queries_session(self):
self.assertTrue(self.mock_init.called) self.assertTrue(self.mock_init.called)
@ -77,20 +52,13 @@ class TestTornadoSession(unittest.TestCase):
class SessionIntegrationTests(unittest.TestCase): class SessionIntegrationTests(unittest.TestCase):
def setUp(self): def setUp(self):
os.environ['TEST4_HOST'] = 'localhost' os.environ['PGSQL_TEST4'] = \
os.environ['TEST4_PORT'] = '5432' 'postgresql://postgres@localhost:5432/postgres'
os.environ['TEST4_DBNAME'] = 'postgres'
os.environ['TEST4_USER'] = 'postgres'
try: try:
self.session = postgresql.Session('test', pool_max_size=10) self.session = postgresql.Session('test4', pool_max_size=10)
except postgresql.OperationalError as error: except postgresql.OperationalError as error:
raise unittest.SkipTest(str(error).split('\n')[0]) raise unittest.SkipTest(str(error).split('\n')[0])
def tearDown(self):
for key in ['HOST', 'PORT', 'DBNAME', 'USER']:
del os.environ['TEST4_%s' % key]
def test_query_returns_results_object(self): def test_query_returns_results_object(self):
self.assertIsInstance(self.session.query('SELECT 1 AS value'), self.assertIsInstance(self.session.query('SELECT 1 AS value'),
queries.Results) queries.Results)
@ -118,18 +86,12 @@ class TornadoSessionIntegrationTests(testing.AsyncTestCase):
def setUp(self): def setUp(self):
super(TornadoSessionIntegrationTests, self).setUp() super(TornadoSessionIntegrationTests, self).setUp()
os.environ['TEST5_HOST'] = 'localhost' os.environ['PGSQL_TEST5'] = \
os.environ['TEST5_PORT'] = '5432' 'postgresql://postgres@localhost:5432/postgres'
os.environ['TEST5_DBNAME'] = 'postgres' self.session = postgresql.TornadoSession('test5',
os.environ['TEST5_USER'] = 'postgres'
self.session = postgresql.TornadoSession('test',
pool_max_size=10, pool_max_size=10,
io_loop=self.io_loop) io_loop=self.io_loop)
#def tearDown(self):
# for key in ['HOST', 'PORT', 'DBNAME', 'USER']:
# del os.environ['TEST5_%s' % key]
@testing.gen_test @testing.gen_test
def test_query_returns_results_object(self): def test_query_returns_results_object(self):
try: try: