diff --git a/README.rst b/README.rst index 4e00ea9..2656db0 100644 --- a/README.rst +++ b/README.rst @@ -26,8 +26,8 @@ Requirements Example ------- -The following example sets the environment variables for connecting to -PostgreSQL on localhost to the ``postgres`` database and issues a query. +The following example sets the environment variable for connecting to +PostgreSQL on localhost to the ``production`` database and issues a query. .. code:: python @@ -35,12 +35,9 @@ PostgreSQL on localhost to the ``postgres`` database and issues a query. from sprockets.clients import postgresql - os.environ['POSTGRES_HOST'] = 'localhost' - os.environ['POSTGRES_USER'] = 'postgres' - os.environ['POSTGRES_PORT'] = 5432 - os.environ['POSTGRES_DBNAME'] = 'postgres' + os.environ['PGSQL_PROD'] = 'postgresql://postgres@localhost:5432/production' - session = postgresql.Session('postgres') + session = postgresql.Session('prod') result = session.query('SELECT 1') print(repr(result)) diff --git a/docs/examples.rst b/docs/examples.rst index 9a38b83..7b3ab36 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -9,12 +9,9 @@ PostgreSQL on localhost to the ``postgres`` database and issues a query. from sprockets.clients import postgresql - os.environ['POSTGRES_HOST'] = 'localhost' - os.environ['POSTGRES_USER'] = 'postgres' - os.environ['POSTGRES_PORT'] = 5432 - os.environ['POSTGRES_DBNAME'] = 'postgres' + os.environ['PGSQL'] = 'postgresql://postgres@localhost:5432/postgres' - session = postgresql.Session('postgres') + session = postgresql.Session() result = session.query('SELECT 1') print(repr(result)) @@ -30,15 +27,12 @@ class in a Tornado :py:class:`RequestHandler `. from sprockets.clients import postgresql from tornado import web - os.environ['POSTGRES_HOST'] = 'localhost' - os.environ['POSTGRES_USER'] = 'postgres' - os.environ['POSTGRES_PORT'] = 5432 - os.environ['POSTGRES_DBNAME'] = 'postgres' + os.environ['PGSQL_FOO'] = 'postgresql://postgres@localhost:5432/foo' class RequestHandler(web.RequestHandler): def initialize(self): - self.session = postgresql.TornadoSession('postgres') + self.session = postgresql.TornadoSession('foo') @gen.coroutine def get(self, *args, **kwargs): diff --git a/docs/history.rst b/docs/history.rst index 1bac87b..4d9f54b 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -1,5 +1,7 @@ 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] - Expose psycopg2/queries exceptions, objects, etc from ``sprockets.queries.postgresql`` - Add integration testing with PostgreSQL diff --git a/setup.py b/setup.py index e796f1a..326f134 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import setuptools setuptools.setup( name='sprockets.clients.postgresql', - version='1.0.1', + version='2.0.0', description=('PostgreSQL client library wrapper providing environment ' 'variable based configuration'), long_description=codecs.open('README.rst', encoding='utf-8').read(), diff --git a/sprockets/clients/postgresql/__init__.py b/sprockets/clients/postgresql/__init__.py index cd992e7..a20eced 100644 --- a/sprockets/clients/postgresql/__init__.py +++ b/sprockets/clients/postgresql/__init__.py @@ -5,19 +5,18 @@ The Session classes wrap the Queries :py:class:`Session ` and :py:class:`TornadoSession ` classes providing environment variable based configuration. -The environment variables should be set using the ``DBNAME_[VARIABLE]`` format -where ``[VARIABLE]`` is one of ``HOST``, ``PORT``, ``DBNAME``, ``USER``, and -``PASSWORD``. +Environment variables should be set using the ``PGSQL[_DBNAME]`` format +where the value is a PostgreSQL URI. -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 - FOO_HOST = 'foodb' - FOO_PORT = '6000' - FOO_DBNAME = 'foo' - FOO_USER = 'bar' - FOO_PASSWORD = 'baz' + PGSQL_FOO = 'postgresql://bar:baz@foohost:6000/foo' and code for creating a :py:class:`Session` instance for the database name ``foo``: @@ -26,11 +25,12 @@ and code for creating a :py:class:`Session` instance for the database name session = sprockets.postgresql.Session('foo') -The uri ``postgresql://bar:baz@foodb:6000/foo`` will be used when creating the -instance of :py:class:`queries.Session`. +A :py:class:`queries.Session` object will be created that connects to Postgres +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) import logging @@ -66,27 +66,21 @@ from queries import TransactionRollbackError def _get_uri(dbname): - """Construct the URI for connecting to PostgreSQL by appending each - argument name to the dbname, delimited by an underscore and - capitalizing the new variable name. - - Values will be retrieved from the environment variable and added to a - dictionary that is then passed in as keyword arguments to the - :py:meth:`queries.uri` method to build the URI string. + """Return the URI for the specified database name from an environment + variable. If dbname is blank, the ``PGSQL`` environment variable is used, + otherwise the database name is cast to upper case and concatenated to + ``PGSQL_`` and the URI is retrieved from ``PGSQL_DBNAME``. For example, + if the value ``foo`` is passed in, the environment variable used would be + ``PGSQL_FOO``. :param str dbname: The database name to construct the URI for :return: str + :raises: KeyError """ - kwargs = dict() - for arg in _ARGUMENTS: - value = os.getenv(('%s_%s' % (dbname, arg)).upper()) - if value: - if arg == 'port': - kwargs[arg] = int(value) - else: - kwargs[arg] = value - return queries.uri(**kwargs) + if not dbname: + return os.environ['PGSQL'] + return os.environ['PGSQL_{0}'.format(dbname).upper()] class Session(queries.Session): diff --git a/tests.py b/tests.py index 1272448..005afe6 100644 --- a/tests.py +++ b/tests.py @@ -10,7 +10,6 @@ try: except ImportError: import unittest -from tornado import gen from sprockets.clients import postgresql import queries from tornado import testing @@ -18,17 +17,9 @@ from tornado import testing 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): - - os.environ['TEST1_HOST'] = 'test1-host' - os.environ['TEST1_PORT'] = '5436' - os.environ['TEST1_DBNAME'] = 'test1' - os.environ['TEST1_USER'] = 'foo1' - os.environ['TEST1_PASSWORD'] = 'baz1' + os.environ['PGSQL_TEST1'] = \ + 'postgresql://foo1:baz1@test1-host:5436/test1' self.assertEqual(postgresql._get_uri('test1'), 'postgresql://foo1:baz1@test1-host:5436/test1') @@ -39,17 +30,9 @@ class TestSession(unittest.TestCase): @mock.patch('queries.session.Session.__init__') def setUp(self, mock_init): self.mock_init = mock_init - os.environ['TEST2_HOST'] = 'db1' - os.environ['TEST2_PORT'] = '5433' - os.environ['TEST2_DBNAME'] = 'bar' - os.environ['TEST2_USER'] = 'foo' - os.environ['TEST2_PASSWORD'] = 'baz' + os.environ['PGSQL_TEST2'] = 'postgresql://foo:baz@db1:5433/bar' 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): self.assertTrue(self.mock_init.called) @@ -59,17 +42,9 @@ class TestTornadoSession(unittest.TestCase): @mock.patch('queries.tornado_session.TornadoSession.__init__') def setUp(self, mock_init): self.mock_init = mock_init - os.environ['TEST3_HOST'] = 'db1' - os.environ['TEST3_PORT'] = '5434' - os.environ['TEST3_DBNAME'] = 'bar' - os.environ['TEST3_USER'] = 'foo' - os.environ['TEST3_PASSWORD'] = 'baz' + os.environ['PGSQL_TEST3'] = 'postgresql://foo:baz@db1:5434/bar' 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): self.assertTrue(self.mock_init.called) @@ -77,20 +52,13 @@ class TestTornadoSession(unittest.TestCase): class SessionIntegrationTests(unittest.TestCase): def setUp(self): - os.environ['TEST4_HOST'] = 'localhost' - os.environ['TEST4_PORT'] = '5432' - os.environ['TEST4_DBNAME'] = 'postgres' - os.environ['TEST4_USER'] = 'postgres' - + os.environ['PGSQL_TEST4'] = \ + 'postgresql://postgres@localhost:5432/postgres' try: - self.session = postgresql.Session('test', pool_max_size=10) + self.session = postgresql.Session('test4', pool_max_size=10) except postgresql.OperationalError as error: 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): self.assertIsInstance(self.session.query('SELECT 1 AS value'), queries.Results) @@ -118,18 +86,12 @@ class TornadoSessionIntegrationTests(testing.AsyncTestCase): def setUp(self): super(TornadoSessionIntegrationTests, self).setUp() - os.environ['TEST5_HOST'] = 'localhost' - os.environ['TEST5_PORT'] = '5432' - os.environ['TEST5_DBNAME'] = 'postgres' - os.environ['TEST5_USER'] = 'postgres' - self.session = postgresql.TornadoSession('test', + os.environ['PGSQL_TEST5'] = \ + 'postgresql://postgres@localhost:5432/postgres' + self.session = postgresql.TornadoSession('test5', pool_max_size=10, io_loop=self.io_loop) - #def tearDown(self): - # for key in ['HOST', 'PORT', 'DBNAME', 'USER']: - # del os.environ['TEST5_%s' % key] - @testing.gen_test def test_query_returns_results_object(self): try: