Skip to content

Commit a5f65fd

Browse files
author
James William Pye
committed
Document the msghook attribute on elements.
Prior it was 'trap_message', but for consistency's sake, use 'msghook'. This illuminates a previously undocumented feature in which the user may install hooks on the elements that "raise" database messages. Notably, the ability to suffocate the message by returning a `True` value.
1 parent 5bb5479 commit a5f65fd

4 files changed

Lines changed: 140 additions & 13 deletions

File tree

postgresql/api.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,20 +144,22 @@ def _e_metas(self):
144144
def raise_message(self, starting_point = None):
145145
"""
146146
Take the given message object and hand it to all the primary
147-
factors(creator) with a trap_message callable.
147+
factors(creator) with a msghook callable.
148148
"""
149149
if starting_point is not None:
150-
current = starting_point
150+
f = starting_point
151151
else:
152-
current = self.creator
152+
f = self.creator
153153

154-
while current is not None:
155-
if getattr(current, 'trap_message', None) is not None:
156-
if f.trap_message(self):
154+
while f is not None:
155+
if getattr(f, 'msghook', None) is not None:
156+
if f.msghook(self):
157157
# the trap returned a nonzero value,
158-
# so don't continue raising.
159-
return current
160-
current = prime_factor(current)
158+
# so don't continue raising. (like with's __exit__)
159+
return f
160+
f = prime_factor(f)
161+
if f:
162+
f = f[1]
161163
# if the next primary factor is without a raise or does not exist,
162164
# send the message to postgresql.sys.msghook
163165
pg_sys.msghook(self)

postgresql/documentation/driver.txt

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ interfaces:
275275
Revocation list file path. [Currently not checked.]
276276

277277
``category``
278-
A `postgresql.api.Category` instance used to execute further database
279-
initialization such as library bindings.
278+
A `postgresql.api.Category` instance used to further initialize
279+
the database.
280280

281281

282282
Connections
@@ -347,6 +347,12 @@ The methods and properties on the connection object are ready for use:
347347
Create a new connection object based on the same factors that were used to
348348
create ``db``. The new connection returned will already be connected.
349349

350+
``db.msghook(msg)``
351+
By default, the `msghook` attribute does not exist. If set to a callable, any
352+
message that occurs during an operation of the database or an operation of a
353+
database derived object will be given to the callable. See the
354+
`Message Management`_ section for more information.
355+
350356

351357
Connection Metadata
352358
-------------------
@@ -513,6 +519,12 @@ Prepared statement objects have a few execution methods:
513519
Create a new statement object based on the same factors that were used to
514520
create ``ps``.
515521

522+
``ps.msghook(msg)``
523+
By default, the `msghook` attribute does not exist. If set to a callable, any
524+
message that occurs during an operation of the statement or an operation of a
525+
statement derived object will be given to the callable. See the
526+
`Message Management`_ section for more information.
527+
516528

517529
Statement Metadata
518530
------------------
@@ -831,6 +843,12 @@ those results:
831843
Create a new cursor object based on the same factors that were used to
832844
create ``c``.
833845

846+
``c.msghook(msg)``
847+
By default, the `msghook` attribute does not exist. If set to a callable, any
848+
message that occurs during an operation of the cursor will be given to the
849+
callable. See the `Message Management`_ section for more information.
850+
851+
834852
Cursors have some additional configuration properties that may be modified
835853
during the use of the cursor:
836854

@@ -1298,6 +1316,11 @@ that change of state.
12981316
Prepare the transaction for the final commit. Once prepared, the second commit
12991317
may be ran to finalize the transaction, ``x.commit()``.
13001318

1319+
``x.msghook(msg)``
1320+
By default, the `msghook` attribute does not exist. If set to a callable, any
1321+
message that occurs during an operation of the transaction will be given to
1322+
the callable. See the `Message Management`_ section for more information.
1323+
13011324
These methods are primarily provided for applications that manage transactions
13021325
in a way that cannot be formed around single, sequential blocks of code.
13031326
Generally, using these methods require additional work to be performed by the
@@ -1605,3 +1628,81 @@ Or if use of a dictionary is desired::
16051628

16061629
When a dictionary is given to construct the row, absent values are filled with
16071630
`None`.
1631+
1632+
1633+
Message Management
1634+
==================
1635+
1636+
By default, py-postgresql gives detailed reports of messages emitted by the
1637+
database. Often, the verbosity is excessive due to single target processes or
1638+
existing application infrastructure for tracing the sources of various events.
1639+
1640+
PostgreSQL itself provides a noise reduction tool for the client via the
1641+
``client_min_messages`` setting. Altering this setting to a preferred level
1642+
either temporarily or permanently can be an appropriate solution in many cases.
1643+
1644+
When finer grained control over message details is needed, py-postgresql's
1645+
object relationship model provides a common protocol for controlling message
1646+
propagation and, ultimately, display.
1647+
1648+
The ``msghook`` attribute on elements is absent by default. However, when
1649+
present on an object--explicitly set--that contributed the cause of a message
1650+
event, it will be invoked with the Message, `postgresql.api.Message`, object as
1651+
its sole parameter. The attribute of the object that is closest to the event is
1652+
checked first, if present it will be called. If the ``msghook()`` call returns
1653+
a `True` value(specficially, ``bool(x) is True``), the message will not be
1654+
propagated any further. However, if a `False` value is returned, the next
1655+
element is checked until the list is exhausted and the message is given to
1656+
`postgresql.sys.msghook`. The normal list of elements is as follows::
1657+
1658+
Output -> Statement -> Connection -> Connector -> Driver [-> postgresql.sys]
1659+
1660+
Where ``Output`` can be a `postgresql.api.Cursor` object produced by
1661+
``declare(...)`` or an implicit output management object used *internally* by
1662+
``__call__()`` and other statement execution methods. Setting the ``msghook``
1663+
attribute on `postgresql.api.PreparedStatment` gives very fine control over
1664+
raised messages. Consider filtering the notice message on create table
1665+
statements that implicitly create indexes::
1666+
1667+
>>> db = postgresql.open(...)
1668+
>>> ct_this = db.prepare('CREATE TEMP TABLE "this" (i int PRIMARY KEY)')
1669+
>>> ct_that = db.prepare('CREATE TEMP TABLE "that" (i int PRIMARY KEY)')
1670+
>>> def filter_notices(msg):
1671+
... if msg.details['severity'] == 'NOTICE':
1672+
... return True
1673+
...
1674+
>>> ct_that()
1675+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "that_pkey" for table "that"
1676+
...
1677+
('CREATE', None)
1678+
>>> ct_this.msghook = filter_notices
1679+
>>> ct_this()
1680+
('CREATE', None)
1681+
>>>
1682+
1683+
The above illustrates the quality of an installed ``msghook`` that simply
1684+
inhibits further propagation messages with a severity of 'NOTICE'--but, only
1685+
notices coming from objects derived from the ``ct_this``
1686+
`postgresql.api.PreparedStatement` object.
1687+
1688+
Subsequently, if the filter is installed on the connection's ``msghook``::
1689+
1690+
>>> db = postgresql.open(...)
1691+
>>> ct_this = db.prepare('CREATE TEMP TABLE "this" (i int PRIMARY KEY)')
1692+
>>> ct_that = db.prepare('CREATE TEMP TABLE "that" (i int PRIMARY KEY)')
1693+
>>> def filter_notices(msg):
1694+
... if msg.details['severity'] == 'NOTICE':
1695+
... return True
1696+
...
1697+
>>> db.msghook = filter_notices
1698+
>>> ct_that()
1699+
('CREATE', None)
1700+
>>> ct_this()
1701+
('CREATE', None)
1702+
>>>
1703+
1704+
Any message with ``'NOTICE'`` severity coming from the connection, ``db``, will be
1705+
suffocated by the ``filter_notices`` function. However, if a ``msghook`` is
1706+
installed on either of those statements, it would be possible for display to
1707+
occur depending on the implementation of the hook installed on the statement
1708+
objects.

postgresql/documentation/gotchas.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,27 @@ is not an string, it will fail to unpack the row or pack the appropriate data fo
6969
the element or attribute.
7070

7171
In most cases issues related to this can be avoided with explicit casts to text.
72+
73+
74+
NOTICEs, WARNINGs, and other messages are too verbose
75+
-----------------------------------------------------
76+
77+
For many situations, the information provided with database messages is
78+
far too verbose. However, considering that py-postgresql is a programmer's
79+
library, the default of high verbosity is taken with the express purpose of
80+
allowing the programmer to "adjust the volume" until appropriate.
81+
82+
There are a number of ways to reduce the noise emitted by a script. In some
83+
cases, the alteration of the ``client_min_messages`` setting is the most
84+
appropriate solution::
85+
86+
>>> db.execute("SET client_min_messages TO WARNING;")
87+
88+
However, py-postgresql provides some additional configuration tools and message
89+
hooks in order to taylor the detail into something more appropriate for a given
90+
application.
91+
92+
See the PostgreSQL documentation for more information on the
93+
``client_min_messages`` setting, and see the Database Messages section in
94+
`postgresql.documentation.driver` for more information on filtering and the
95+
`postgresql.sys.msghook`.

postgresql/sys.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717

1818
libpath = []
1919

20-
def default_msghook(msg):
20+
def default_msghook(msg, format_message = format_element):
2121
"""
2222
Built-in message hook. DON'T TOUCH!
2323
"""
2424
if sys.stderr and not sys.stderr.closed:
2525
try:
26-
sys.stderr.write(format_element(msg) + os.linesep)
26+
sys.stderr.write(format_message(msg) + os.linesep)
2727
except Exception:
2828
try:
2929
sys.excepthook(*sys.exc_info())

0 commit comments

Comments
 (0)