Skip to content

Commit a151f99

Browse files
author
James William Pye
committed
Use the errno's that are *not* fatal to identify the fatal ones.
The fatal list was far too long. Also, use os.strerror to get the error's string rather than writing our own. fixes #41
1 parent a9a40a3 commit a151f99

3 files changed

Lines changed: 41 additions & 30 deletions

File tree

postgresql/exceptions.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
##
2-
# copyright 2009, James William Pye
3-
# http://python.projects.postgresql.org
2+
# .exceptions - Exception hierarchy for PostgreSQL database ERRORs.
43
##
54
"""
65
PostgreSQL exceptions and warnings with associated state codes.

postgresql/python/socket.py

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
##
2-
# copyright 2009, James William Pye
3-
# http://python.projects.postgresql.org
2+
# .python.socket - additional tools for working with sockets
43
##
5-
"""
6-
socket tools
7-
"""
84
import sys
5+
import os
96
import random
107
import socket
118
import math
@@ -23,46 +20,38 @@ class SocketFactory(object):
2320
Additionally, it provides methods and attributes for abstracting
2421
exception management on socket operation.
2522
"""
26-
fatal_exception_messages = {
27-
errno.ECONNRESET : 'server explicitly closed the connection',
28-
errno.EPIPE : 'broken connection detected on send',
29-
errno.ECONNREFUSED : 'server refused connection',
30-
errno.EHOSTUNREACH : 'server is not reachable',
31-
errno.EBADF : 'bad file descriptor',
32-
}
33-
if sys.platform in ('win32', 'win64'):
34-
fatal_exception_messages.update({
35-
errno.WSAECONNABORTED : 'server aborted the connection',
36-
})
3723

3824
timeout_exception = socket.timeout
3925
fatal_exception = socket.error
4026
try_again_exception = socket.error
4127

4228
def timed_out(self, err) -> bool:
43-
return type(err) is self.timeout_exception
29+
return err.__class__ is self.timeout_exception
4430

45-
def try_again(self, err) -> bool:
31+
@staticmethod
32+
def try_again(err, codes = (errno.EAGAIN, errno.EINTR, errno.EWOULDBLOCK, errno.ETIMEDOUT)) -> bool:
4633
"""
47-
Does the error indicate that the operation should be
48-
tried again?
49-
"""
50-
return getattr(err, 'errno', 0) == errno.EINTR
34+
Does the error indicate that the operation should be tried again?
5135
52-
def connection_refused(self, err) -> bool:
53-
"""
54-
Does the error indicate that the connection was explicitly
55-
refused by the server?
36+
More importantly, the connection is *not* dead.
5637
"""
57-
return getattr(err, 'errno', 0) == errno.ECONNREFUSED
38+
errno = getattr(err, 'errno', None)
39+
if errno is None:
40+
return False
41+
return errno in codes
5842

5943
@classmethod
6044
def fatal_exception_message(typ, err) -> (str, None):
6145
"""
6246
If the exception was fatal to the connection,
6347
what message should be given to the user?
6448
"""
65-
return typ.fatal_exception_messages.get(err.errno)
49+
if typ.try_again(err):
50+
return None
51+
strerr = getattr(err, 'strerror', None)
52+
if strerr is not None:
53+
return str(strerr)
54+
return os.strerror(err.errno)
6655

6756
def secure(self, socket : socket.socket) -> ssl.SSLSocket:
6857
"secure a socket with SSL"

postgresql/test/test_driver.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,5 +1694,28 @@ def testNamedTuples(self):
16941694
self.failUnlessEqual(r[2], "hello")
16951695
self.failUnlessEqual(r.param, "hello")
16961696

1697+
@pg_tmp
1698+
def testBadFD(self):
1699+
db.pq.socket.close()
1700+
# bad fd now.
1701+
self.failUnlessRaises(
1702+
pg_exc.ConnectionFailureError,
1703+
sqlexec, "SELECT 1"
1704+
)
1705+
self.failUnless(issubclass(pg_exc.ConnectionFailureError, pg_exc.Disconnection))
1706+
1707+
@pg_tmp
1708+
def testAdminTerminated(self):
1709+
killer = new()
1710+
killer.sys.terminate_backends()
1711+
# hoping that this will guarantee that the terminate is complete
1712+
killer.close()
1713+
1714+
self.failUnlessRaises(
1715+
pg_exc.AdminShutdownError,
1716+
sqlexec, "SELECT 1"
1717+
)
1718+
self.failUnless(issubclass(pg_exc.AdminShutdownError, pg_exc.Disconnection))
1719+
16971720
if __name__ == '__main__':
16981721
unittest.main()

0 commit comments

Comments
 (0)