Changeset 745 for branches/4.x


Ignore:
Timestamp:
Jan 14, 2016, 8:16:23 PM (4 years ago)
Author:
cito
Message:

Add methods get/set_parameter to DB wrapper class

These methods can be used to get/set/reset run-time parameters,
even several at once.

Since this is pretty useful and will not break anything, I have
also back ported these additions to the 4.x branch.

Everything is well documented and tested, of course.

Location:
branches/4.x
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • branches/4.x/docs/contents/changelog.rst

    r709 r745  
    44Version 4.2
    55-----------
    6 - Set a better default for the user option "escaping-funcs".
    76- The supported Python versions are 2.4 to 2.7.
    87- PostgreSQL is supported in all versions from 8.3 to 9.5.
     8- Set a better default for the user option "escaping-funcs".
    99- Force build to compile with no errors.
     10- New methods get_parameters() and set_parameters() in the classic interface
     11  which can be used to get or set run-time parameters.
    1012- Fix decimal point handling.
    1113- Add option to return boolean values as bool objects.
  • branches/4.x/docs/contents/pg/db_wrapper.rst

    r738 r745  
    122122
    123123    :param str table: name of table
    124     :returns: A dictionary -- the keys are the attribute names,
    125      the values are the type names of the attributes.
     124    :returns: a dictionary mapping attribute names to type names
    126125
    127126Given the name of a table, digs out the set of attribute names.
     127
     128Returns a dictionary of attribute names (the names are the keys,
     129the values are the names of the attributes' types).
     130
     131By default, only a limited number of simple types will be returned.
     132You can get the regular types after enabling this by calling the
     133:meth:`DB.use_regtypes` method.
     134
     135get/set_parameter -- get or set  run-time parameters
     136----------------------------------------------------
     137
     138.. method:: DB.get_parameter(parameter)
     139
     140    Get the value of run-time parameters
     141
     142    :param parameter: the run-time parameter(s) to get
     143    :type param: str, tuple, list or dict
     144    :returns: the current value(s) of the run-time parameter(s)
     145    :rtype: str, list or dict
     146    :raises TypeError: Invalid parameter type(s)
     147    :raises ProgrammingError: Invalid parameter name(s)
     148
     149If the parameter is a string, the return value will also be a string
     150that is the current setting of the run-time parameter with that name.
     151
     152You can get several parameters at once by passing a list or tuple of
     153parameter names.  The return value will then be a corresponding list
     154of parameter settings.  If you pass a dict as parameter instead, its
     155values will be set to the parameter settings corresponding to its keys.
     156
     157By passing the special name `'all'` as the parameter, you can get a dict
     158of all existing configuration parameters.
     159
     160.. versionadded:: 4.2
     161
     162.. method:: DB.set_parameter(self, parameter, [value], [local])
     163
     164    Set the value of run-time parameters
     165
     166    :param parameter: the run-time parameter(s) to set
     167    :type param: string, tuple, list or dict
     168    :param value: the value to set
     169    :type param: str or None
     170    :raises TypeError: Invalid parameter type(s)
     171    :raises ValueError: Invalid value argument(s)
     172    :raises ProgrammingError: Invalid parameter name(s) or values
     173
     174If the parameter and the value are strings, the run-time parameter
     175will be set to that value.  If no value or *None* is passed as a value,
     176then the run-time parameter will be restored to its default value.
     177
     178You can set several parameters at once by passing a list or tuple
     179of parameter names, with a single value that all parameters should
     180be set to or with a corresponding list or tuple of values.
     181
     182You can also pass a dict as parameters.  In this case, you should
     183not pass a value, since the values will be taken from the dict.
     184
     185By passing the special name `'all'` as the parameter, you can reset
     186all existing settable run-time parameters to their default values.
     187
     188If you set *local* to `True`, then the command takes effect for only the
     189current transaction.  After :meth:`DB.commit` or :meth:`DB.rollback`,
     190the session-level setting takes effect again.  Setting *local* to `True`
     191will appear to have no effect if it is executed outside a transaction,
     192since the transaction will end immediately.
     193
     194.. versionadded:: 4.2
    128195
    129196has_table_privilege -- check table privilege
  • branches/4.x/pg.py

    r738 r745  
    517517        """Destroy a previously defined savepoint."""
    518518        return self.query('RELEASE ' + name)
     519
     520    def get_parameter(self, parameter):
     521        """Get the value of a run-time parameter.
     522
     523        If the parameter is a string, the return value will also be a string
     524        that is the current setting of the run-time parameter with that name.
     525
     526        You can get several parameters at once by passing a list or tuple of
     527        parameter names.  The return value will then be a corresponding list
     528        of parameter settings.  If you pass a dict as parameter instead, its
     529        values will be set to the parameter settings corresponding to its keys.
     530
     531        By passing the special name 'all' as the parameter, you can get a dict
     532        of all existing configuration parameters.
     533        """
     534        if isinstance(parameter, basestring):
     535            parameter = [parameter]
     536            values = None
     537        elif isinstance(parameter, (list, tuple)):
     538            values = []
     539        elif isinstance(parameter, dict):
     540            values = parameter
     541        else:
     542            raise TypeError('The parameter must be a dict, list or string')
     543        if not parameter:
     544            raise TypeError('No parameter has been specified')
     545        if isinstance(values, dict):
     546            params = {}
     547        else:
     548            params = []
     549        for key in parameter:
     550            if isinstance(key, basestring):
     551                param = key.strip().lower()
     552            else:
     553                param = None
     554            if not param:
     555                raise TypeError('Invalid parameter')
     556            if param == 'all':
     557                q = 'SHOW ALL'
     558                values = self.db.query(q).getresult()
     559                values = dict(value[:2] for value in values)
     560                break
     561            if isinstance(values, dict):
     562                params[param] = key
     563            else:
     564                params.append(param)
     565        else:
     566            for param in params:
     567                q = 'SHOW %s' % (param,)
     568                value = self.db.query(q).getresult()[0][0]
     569                if values is None:
     570                    values = value
     571                elif isinstance(values, list):
     572                    values.append(value)
     573                else:
     574                    values[params[param]] = value
     575        return values
     576
     577    def set_parameter(self, parameter, value=None, local=False):
     578        """Set the value of a run-time parameter.
     579
     580        If the parameter and the value are strings, the run-time parameter
     581        will be set to that value.  If no value or None is passed as a value,
     582        then the run-time parameter will be restored to its default value.
     583
     584        You can set several parameters at once by passing a list or tuple
     585        of parameter names, with a single value that all parameters should
     586        be set to or with a corresponding list or tuple of values.
     587
     588        You can also pass a dict as parameters.  In this case, you should
     589        not pass a value, since the values will be taken from the dict.
     590
     591        By passing the special name 'all' as the parameter, you can reset
     592        all existing settable run-time parameters to their default values.
     593
     594        If you set local to True, then the command takes effect for only the
     595        current transaction.  After commit() or rollback(), the session-level
     596        setting takes effect again.  Setting local to True will appear to
     597        have no effect if it is executed outside a transaction, since the
     598        transaction will end immediately.
     599        """
     600        if isinstance(parameter, basestring):
     601            parameter = {parameter: value}
     602        elif isinstance(parameter, (list, tuple)):
     603            if isinstance(value, (list, tuple)):
     604                parameter = dict(zip(parameter, value))
     605            else:
     606                parameter = dict.fromkeys(parameter, value)
     607        elif isinstance(parameter, dict):
     608            if value is not None:
     609                raise ValueError(
     610                    'A value must not be set when parameter is a dictionary')
     611        else:
     612            raise TypeError('The parameter must be a dict, list or string')
     613        if not parameter:
     614            raise TypeError('No parameter has been specified')
     615        params = {}
     616        for key, value in parameter.items():
     617            if isinstance(key, basestring):
     618                param = key.strip().lower()
     619            else:
     620                param = None
     621            if not param:
     622                raise TypeError('Invalid parameter')
     623            if param == 'all':
     624                if value is not None:
     625                    raise ValueError(
     626                        "A value must ot be set when parameter is 'all'")
     627                params = {'all': None}
     628                break
     629            params[param] = value
     630        local = local and ' LOCAL' or ''
     631        for param, value in params.items():
     632            if value is None:
     633                q = 'RESET%s %s' % (local, param)
     634            else:
     635                q = 'SET%s %s TO %s' % (local, param, value)
     636            self._do_debug(q)
     637            self.db.query(q)
    519638
    520639    def query(self, qstr, *args):
  • branches/4.x/tests/test_classic_dbwrapper.py

    r743 r745  
    7373        attributes = [
    7474            'begin',
    75             'cancel',
    76             'clear',
    77             'close',
    78             'commit',
    79             'db',
    80             'dbname',
    81             'debug',
    82             'delete',
    83             'end',
    84             'endcopy',
    85             'error',
    86             'escape_bytea',
    87             'escape_identifier',
    88             'escape_literal',
    89             'escape_string',
     75            'cancel', 'clear', 'close', 'commit',
     76            'db', 'dbname', 'debug', 'delete',
     77            'end', 'endcopy', 'error',
     78            'escape_bytea', 'escape_identifier',
     79            'escape_literal', 'escape_string',
    9080            'fileno',
    91             'get',
    92             'get_attnames',
    93             'get_databases',
    94             'get_notice_receiver',
    95             'get_relations',
    96             'get_tables',
    97             'getline',
    98             'getlo',
    99             'getnotify',
    100             'has_table_privilege',
    101             'host',
    102             'insert',
    103             'inserttable',
    104             'locreate',
    105             'loimport',
     81            'get', 'get_attnames', 'get_databases',
     82            'get_notice_receiver', 'get_parameter',
     83            'get_relations', 'get_tables',
     84            'getline', 'getlo', 'getnotify',
     85            'has_table_privilege', 'host',
     86            'insert', 'inserttable',
     87            'locreate', 'loimport',
    10688            'notification_handler',
    10789            'options',
    108             'parameter',
    109             'pkey',
    110             'port',
    111             'protocol_version',
    112             'putline',
     90            'parameter', 'pkey', 'port',
     91            'protocol_version', 'putline',
    11392            'query',
    114             'release',
    115             'reopen',
    116             'reset',
    117             'rollback',
    118             'savepoint',
    119             'server_version',
    120             'set_notice_receiver',
    121             'source',
    122             'start',
    123             'status',
    124             'transaction',
    125             'tty',
    126             'unescape_bytea',
    127             'update',
    128             'use_regtypes',
    129             'user',
     93            'release', 'reopen', 'reset', 'rollback',
     94            'savepoint', 'server_version',
     95            'set_notice_receiver', 'set_parameter',
     96            'source', 'start', 'status',
     97            'transaction', 'tty',
     98            'unescape_bytea', 'update',
     99            'use_regtypes', 'user',
    130100        ]
    131101        if self.db.server_version < 90000:  # PostgreSQL < 9.0
     
    289259        db.query("create table test ("
    290260            "i2 smallint, i4 integer, i8 bigint,"
    291             "d numeric, f4 real, f8 double precision, m money, "
    292             "v4 varchar(4), c4 char(4), t text)")
     261            " d numeric, f4 real, f8 double precision, m money,"
     262            " v4 varchar(4), c4 char(4), t text)")
    293263        db.query("create or replace view test_view as"
    294264            " select i4, v4 from test")
     
    442412        self.assertEqual(f("a\\b'c", 'text'), "'a\\\\b''c'")
    443413
     414    def testGetParameter(self):
     415        f = self.db.get_parameter
     416        self.assertRaises(TypeError, f)
     417        self.assertRaises(TypeError, f, None)
     418        self.assertRaises(TypeError, f, 42)
     419        self.assertRaises(pg.ProgrammingError, f, 'this_does_not_exist')
     420        r = f('standard_conforming_strings')
     421        self.assertEqual(r, 'on')
     422        r = f('lc_monetary')
     423        self.assertEqual(r, 'C')
     424        r = f('datestyle')
     425        self.assertEqual(r, 'ISO, YMD')
     426        r = f('bytea_output')
     427        self.assertEqual(r, 'hex')
     428        r = f(('bytea_output', 'lc_monetary'))
     429        self.assertIsInstance(r, list)
     430        self.assertEqual(r, ['hex', 'C'])
     431        r = f(['standard_conforming_strings', 'datestyle', 'bytea_output'])
     432        self.assertEqual(r, ['on', 'ISO, YMD', 'hex'])
     433        s = dict.fromkeys(('bytea_output', 'lc_monetary'))
     434        r = f(s)
     435        self.assertIs(r, s)
     436        self.assertEqual(r, {'bytea_output': 'hex', 'lc_monetary': 'C'})
     437        s = dict.fromkeys(('Bytea_Output', 'LC_Monetary'))
     438        r = f(s)
     439        self.assertIs(r, s)
     440        self.assertEqual(r, {'Bytea_Output': 'hex', 'LC_Monetary': 'C'})
     441
     442    def testGetParameterServerVersion(self):
     443        r = self.db.get_parameter('server_version_num')
     444        self.assertIsInstance(r, str)
     445        s = self.db.server_version
     446        self.assertIsInstance(s, int)
     447        self.assertEqual(r, str(s))
     448
     449    def testGetParameterAll(self):
     450        f = self.db.get_parameter
     451        r = f('all')
     452        self.assertIsInstance(r, dict)
     453        self.assertEqual(r['standard_conforming_strings'], 'on')
     454        self.assertEqual(r['lc_monetary'], 'C')
     455        self.assertEqual(r['DateStyle'], 'ISO, YMD')
     456        self.assertEqual(r['bytea_output'], 'hex')
     457
     458    def testSetParameter(self):
     459        f = self.db.set_parameter
     460        g = self.db.get_parameter
     461        self.assertRaises(TypeError, f)
     462        self.assertRaises(TypeError, f, None)
     463        self.assertRaises(TypeError, f, 42)
     464        self.assertRaises(pg.ProgrammingError, f, 'this_does_not_exist')
     465        f('standard_conforming_strings', 'off')
     466        self.assertEqual(g('standard_conforming_strings'), 'off')
     467        f('datestyle', 'ISO, DMY')
     468        self.assertEqual(g('datestyle'), 'ISO, DMY')
     469        f(('standard_conforming_strings', 'datestyle'), ('on', 'ISO, YMD'))
     470        self.assertEqual(g('standard_conforming_strings'), 'on')
     471        self.assertEqual(g('datestyle'), 'ISO, YMD')
     472        f(['standard_conforming_strings', 'datestyle'], ['off', 'ISO, DMY'])
     473        self.assertEqual(g('standard_conforming_strings'), 'off')
     474        self.assertEqual(g('datestyle'), 'ISO, DMY')
     475        f({'standard_conforming_strings': 'on', 'datestyle': 'ISO, YMD'})
     476        self.assertEqual(g('standard_conforming_strings'), 'on')
     477        self.assertEqual(g('datestyle'), 'ISO, YMD')
     478        f(('default_with_oids', 'standard_conforming_strings'), 'off')
     479        self.assertEqual(g('default_with_oids'), 'off')
     480        self.assertEqual(g('standard_conforming_strings'), 'off')
     481        f(['default_with_oids', 'standard_conforming_strings'], 'on')
     482        self.assertEqual(g('default_with_oids'), 'on')
     483        self.assertEqual(g('standard_conforming_strings'), 'on')
     484
     485    def testResetParameter(self):
     486        db = DB()
     487        f = db.set_parameter
     488        g = db.get_parameter
     489        r = g('default_with_oids')
     490        self.assertIn(r, ('on', 'off'))
     491        dwi, not_dwi = r, r == 'on' and 'off' or 'on'
     492        r = g('standard_conforming_strings')
     493        self.assertIn(r, ('on', 'off'))
     494        scs, not_scs = r, r == 'on' and 'off' or 'on'
     495        f('default_with_oids', not_dwi)
     496        f('standard_conforming_strings', not_scs)
     497        self.assertEqual(g('default_with_oids'), not_dwi)
     498        self.assertEqual(g('standard_conforming_strings'), not_scs)
     499        f('default_with_oids')
     500        f('standard_conforming_strings', None)
     501        self.assertEqual(g('default_with_oids'), dwi)
     502        self.assertEqual(g('standard_conforming_strings'), scs)
     503        f('default_with_oids', not_dwi)
     504        f('standard_conforming_strings', not_scs)
     505        self.assertEqual(g('default_with_oids'), not_dwi)
     506        self.assertEqual(g('standard_conforming_strings'), not_scs)
     507        f(('default_with_oids', 'standard_conforming_strings'))
     508        self.assertEqual(g('default_with_oids'), dwi)
     509        self.assertEqual(g('standard_conforming_strings'), scs)
     510        f('default_with_oids', not_dwi)
     511        f('standard_conforming_strings', not_scs)
     512        self.assertEqual(g('default_with_oids'), not_dwi)
     513        self.assertEqual(g('standard_conforming_strings'), not_scs)
     514        f(['default_with_oids', 'standard_conforming_strings'], None)
     515        self.assertEqual(g('default_with_oids'), dwi)
     516        self.assertEqual(g('standard_conforming_strings'), scs)
     517
     518    def testResetParameterAll(self):
     519        db = DB()
     520        f = db.set_parameter
     521        self.assertRaises(ValueError, f, 'all', 0)
     522        self.assertRaises(ValueError, f, 'all', 'off')
     523        g = db.get_parameter
     524        r = g('default_with_oids')
     525        self.assertIn(r, ('on', 'off'))
     526        dwi, not_dwi = r, r == 'on' and 'off' or 'on'
     527        r = g('standard_conforming_strings')
     528        self.assertIn(r, ('on', 'off'))
     529        scs, not_scs = r, r == 'on' and 'off' or 'on'
     530        f('default_with_oids', not_dwi)
     531        f('standard_conforming_strings', not_scs)
     532        self.assertEqual(g('default_with_oids'), not_dwi)
     533        self.assertEqual(g('standard_conforming_strings'), not_scs)
     534        f('all')
     535        self.assertEqual(g('default_with_oids'), dwi)
     536        self.assertEqual(g('standard_conforming_strings'), scs)
     537
     538    def testSetParameterLocal(self):
     539        f = self.db.set_parameter
     540        g = self.db.get_parameter
     541        self.assertEqual(g('standard_conforming_strings'), 'on')
     542        self.db.begin()
     543        f('standard_conforming_strings', 'off', local=True)
     544        self.assertEqual(g('standard_conforming_strings'), 'off')
     545        self.db.end()
     546        self.assertEqual(g('standard_conforming_strings'), 'on')
     547
     548    def testSetParameterSession(self):
     549        f = self.db.set_parameter
     550        g = self.db.get_parameter
     551        self.assertEqual(g('standard_conforming_strings'), 'on')
     552        self.db.begin()
     553        f('standard_conforming_strings', 'off', local=False)
     554        self.assertEqual(g('standard_conforming_strings'), 'off')
     555        self.db.end()
     556        self.assertEqual(g('standard_conforming_strings'), 'off')
     557
    444558    def testQuery(self):
    445559        query = self.db.query
     
    530644            "c smallint, d smallint primary key)")
    531645        query("create table pkeytest3 ("
    532             "e smallint, f smallint, g smallint, "
    533             "h smallint, i smallint, "
    534             "primary key (f,h))")
     646            "e smallint, f smallint, g smallint,"
     647            " h smallint, i smallint,"
     648            " primary key (f,h))")
    535649        pkey = self.db.pkey
    536650        self.assertRaises(KeyError, pkey, 'pkeytest0')
     
    610724        self.assertNotIn('public.test_view', result)
    611725
    612     def testAttnames(self):
     726    def testGetAttnames(self):
    613727        self.assertRaises(pg.ProgrammingError,
    614728            self.db.get_attnames, 'does_not_exist')
     
    618732            self.db.query('drop table if exists "%s"' % table)
    619733            self.db.query('create table "%s" ('
    620                 'a smallint, b integer, c bigint, '
    621                 'e numeric, f float, f2 double precision, m money, '
    622                 'x smallint, y smallint, z smallint, '
    623                 'Normal_NaMe smallint, "Special Name" smallint, '
    624                 't text, u char(2), v varchar(2), '
    625                 'primary key (y, u)) with oids' % table)
     734                ' a smallint, b integer, c bigint,'
     735                ' e numeric, f float, f2 double precision, m money,'
     736                ' x smallint, y smallint, z smallint,'
     737                ' Normal_NaMe smallint, "Special Name" smallint,'
     738                ' t text, u char(2), v varchar(2),'
     739                ' primary key (y, u)) with oids' % table)
    626740            attributes = self.db.get_attnames(table)
    627741            result = {'a': 'int', 'c': 'int', 'b': 'int',
     
    748862        try:
    749863            get('test_students', "D' Arcy")
    750         except pg.DatabaseError as error:
     864        except pg.DatabaseError, error:
    751865            self.assertEqual(str(error),
    752866                'No such record in public.test_students where firstname = '
     
    754868        try:
    755869            get('test_students', "Robert'); TRUNCATE TABLE test_students;--")
    756         except pg.DatabaseError as error:
     870        except pg.DatabaseError, error:
    757871            self.assertEqual(str(error),
    758872                'No such record in public.test_students where firstname = '
  • branches/4.x/tests/test_tutorial.py

    r716 r745  
    6565        if namedtuple:
    6666            data = list(enumerate(more_fruits, start=3))
    67         else:  # Pyton < 2.6
     67        else:  # Python < 2.6
    6868            data = [(n + 3, name) for n, name in enumerate(more_fruits)]
    6969        db.inserttable('fruits', data)
     
    8888        try:
    8989            rows = r = q.namedresult()
    90         except TypeError:  # Python < 2.6
    91             self.assertIsNone(namedtuple)
    92         else:
    9390            self.assertIsInstance(r, list)
    9491            self.assertIsInstance(r[0], tuple)
    9592            self.assertEqual(rows[3].name, 'durian')
     93        except (AttributeError, TypeError):  # Python < 2.6
     94            self.assertIsNone(namedtuple)
    9695        r = db.update('fruits', banana, name=banana['name'].capitalize())
    9796        self.assertIsInstance(r, dict)
Note: See TracChangeset for help on using the changeset viewer.