Skip to content

Commit 65f8aa3

Browse files
author
James William Pye
committed
Refactor InterfaceElement in order to provide "multiple ancestors".
The InterfaceElement ABC has be moved to postgresql.python.element.Element. "Ancestors" are no more. Rather, they are called factors. As in, the factors that play a critical role in the definition of this element. ife_snapshot_text was completely removed in favor of postgresql.python.element.format_element and the _e_metas methods. format_element takes care of all the formatting cases for the printing of the factor tree, and _e_metas provides any additional, non-factor information about a given element. This makes adding information points *much* easier as snapshot_text required that the element do all of the formatting. Message raising is now supported by checking for the "trap_message" attribute on a transaction's controller and *all* of its primary factors. Like with-statement's exit, a message will not raised if a True value is returned by a given "trap_message" callable. If a message is not suffocated, it will eventually be given to `postgresql.sys.msghook`, which can be overridden by the user. Warnings are no longer Python Warnings, just Message sub-classes. Using Python warnings was pointless. Connection failures are not longer published on CCC errors. They are now published on the connection object itself using postgresql.protocol.client3.ConnectionAttempt objects. This object is temporary, so it will be undocumented like CCC's connection_failure attribute. It may not even make it into 0.9.
1 parent 1424466 commit 65f8aa3

18 files changed

Lines changed: 976 additions & 968 deletions

postgresql/api.py

Lines changed: 129 additions & 421 deletions
Large diffs are not rendered by default.

postgresql/bin/pg_python.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,13 @@ def command(argv = sys.argv):
5555
connection = connector()
5656
connection.connect()
5757
except pg_exc.ClientCannotConnectError as err:
58-
for (didssl, sc, cf) in err.connection_failures:
59-
if isinstance(cf, pg_exc.AuthenticationSpecificationError):
60-
sys.stderr.write(os.linesep + cf.message + (os.linesep*2))
58+
for att in connection.attempt:
59+
exc = att.exception
60+
if isinstance(exc, pg_exc.AuthenticationSpecificationError):
61+
sys.stderr.write(os.linesep + exc.message + (os.linesep*2))
6162
# keep prompting the user
6263
need_prompt = True
64+
connection = None
6365
break
6466
else:
6567
# no invalid password failures..

postgresql/cluster.py

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,23 @@ class Cluster(pg_api.Cluster):
5757
accommodate for a particular purpose.
5858
"""
5959
installation = None
60+
data_directory = None
6061
DEFAULT_CLUSTER_ENCODING = DEFAULT_CLUSTER_ENCODING
6162
DEFAULT_CONFIG_FILENAME = DEFAULT_CONFIG_FILENAME
6263
DEFAULT_PID_FILENAME = DEFAULT_PID_FILENAME
6364
DEFAULT_HBA_FILENAME = DEFAULT_HBA_FILENAME
6465

65-
ife_ancestor = property(attrgetter('installation'))
66-
def ife_snapshot_text(self):
67-
return self.data_directory + ' [' + (
68-
'running: ' + str(self.pid)
69-
if self.running() else 'not running'
70-
) + ']'
66+
@property
67+
def state(self):
68+
if self.running():
69+
return 'running'
70+
return 'not running'
71+
72+
def _e_metas(self):
73+
state = self.state
74+
yield (None, '[' + state + ']')
75+
if state == 'running':
76+
yield ('pid', self.state)
7177

7278
@property
7379
def daemon_path(self):
@@ -169,9 +175,9 @@ def init(self,
169175
if initdb is None:
170176
e = pg_exc.ClusterInitializationError(
171177
"unable to find `initdb` executable for installation: " + \
172-
repr(self.installation)
178+
repr(self.installation),
179+
creator = self
173180
)
174-
self.ife_descend(e)
175181
e.raise_exception()
176182

177183
# Transform keyword options into command options for the executable.
@@ -231,15 +237,14 @@ def init(self,
231237
msg = os.linesep.join([
232238
repr(x)[2:-1] for x in r.splitlines()
233239
])
234-
e = pg_exc.InitDBError(
240+
pg_exc.InitDBError(
235241
msg,
236242
details = {
237243
'COMMAND': cmd,
238244
'RESULT': rc,
239-
}
240-
)
241-
self.ife_descend(e)
242-
e.raise_exception()
245+
},
246+
creator = self
247+
).raise_exception()
243248

244249
def drop(self):
245250
"""
@@ -254,14 +259,13 @@ def drop(self):
254259
try:
255260
self.wait_until_stopped()
256261
except pg_exc.ClusterTimeoutError:
257-
w = pg_exc.ClusterWarning(
262+
pg_exc.ClusterWarning(
258263
'cluster failed to shutdown after kill',
259264
details = {
260265
'hint' : 'Shared memory may be leaked.'
261-
}
262-
)
263-
self.ife_descend(w)
264-
w.emit()
266+
},
267+
creator = self
268+
).raise_message()
265269
# Really, using rm -rf would be the best, but use this for portability.
266270
for root, dirs, files in os.walk(self.data_directory, topdown = False):
267271
for name in files:
@@ -307,9 +311,10 @@ def restart(self, logfile = None, settings = None, timeout = 10):
307311
self.stop()
308312
self.wait_until_stopped(timeout = timeout)
309313
if self.running():
310-
e = pg_exc.ClusterError("failed to shutdown cluster")
311-
self.ife_descend(e)
312-
e.raise_exception()
314+
pg_exc.ClusterError(
315+
"failed to shutdown cluster",
316+
creator = self
317+
).raise_exception()
313318
self.start(logfile = logfile, settings = settings)
314319
self.wait_until_started(timeout = timeout)
315320

@@ -420,11 +425,10 @@ def connect(self, **kw):
420425
Cluster must be running.
421426
"""
422427
if not self.running():
423-
e = ClusterNotRunningError(
424-
"cannot connect if cluster is not running"
425-
)
426-
self.ife_descend(e)
427-
e.raise_exception()
428+
ClusterNotRunningError(
429+
"cannot connect if cluster is not running",
430+
creator = self
431+
).raise_exception()
428432
return self.connection(**kw).connect()
429433

430434
def address(self):
@@ -469,7 +473,8 @@ def ready_for_connections(self):
469473
sslmode = 'disable',
470474
).close()
471475
except pg_exc.ClientCannotConnectError as err:
472-
for (ssltried, sockc, x) in err.connection_failures:
476+
for attempt in err.database.attempt:
477+
x = attempt.exception
473478
if self.installation.version_info[:2] < (8,1):
474479
if isinstance(x, (
475480
pg_exc.UndefinedObjectError,
@@ -515,16 +520,14 @@ def wait_until_started(self,
515520
details = {
516521
'RESULT' : r,
517522
'COMMAND' : self.daemon_command,
518-
}
519-
)
520-
self.ife_descend(e)
521-
e.raise_exception()
523+
},
524+
creator = self
525+
).raise_exception()
522526
else:
523-
e = pg_exc.ClusterNotRunningError(
524-
"postgresql daemon has not been started"
525-
)
526-
self.ife_descend(e)
527-
return e.raise_exception()
527+
pg_exc.ClusterNotRunningError(
528+
"postgresql daemon has not been started",
529+
creator = self
530+
).raise_exception()
528531
r = self.ready_for_connections()
529532

530533
checkpoint = time.time()
@@ -537,9 +540,10 @@ def wait_until_started(self,
537540
# condition, rather it's *still* starting up.
538541
if r is not None and isinstance(r, pg_exc.ServerNotReadyError):
539542
raise r
540-
e = pg_exc.ClusterTimeoutError('timeout on startup')
541-
self.ife_descend(e)
542-
return e.raise_exception(
543+
return pg_exc.ClusterTimeoutError(
544+
'timeout on startup',
545+
creator = self
546+
).raise_exception(
543547
raise_from = r if r not in (True,False) else None
544548
)
545549
time.sleep(delay)
@@ -562,9 +566,10 @@ def wait_until_stopped(self,
562566
if self.daemon_process is not None:
563567
self.last_exit_code = self.daemon_process.poll()
564568
if time.time() - start >= timeout:
565-
e = pg_exc.ClusterTimeoutError('timeout on shutdown')
566-
self.ife_descend(e)
567-
e.raise_exception()
569+
pg_exc.ClusterTimeoutError(
570+
'timeout on shutdown',
571+
creator = self,
572+
).raise_exception()
568573
time.sleep(delay)
569574
##
570575
# vim: ts=3:sw=3:noet:

postgresql/configfile.py

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -216,17 +216,14 @@ class ConfigFile(pg_api.Settings):
216216
Every action will cause the file to be wholly read, so using `update` to make
217217
multiple changes is desirable.
218218
"""
219-
ife_ancestor = None
220-
ife_label = 'CONFIGFILE'
219+
_e_factors = ('path',)
220+
_e_label = 'CONFIGFILE'
221221

222-
def ife_snapshot_text(self):
223-
s = (os.linesep+' ').join(
224-
k + ' = ' + v for k,v in self.items()
225-
)
226-
return self._path + (os.linesep+' ' + s if s else '')
222+
def _e_metas(self):
223+
yield (None, len(self.keys()))
227224

228225
def __init__(self, path, open = open):
229-
self._path = path
226+
self.path = path
230227
self._open = open
231228
self._store = []
232229
self._restore = {}
@@ -235,22 +232,22 @@ def __repr__(self):
235232
return "%s.%s(%r)" %(
236233
type(self).__module__,
237234
type(self).__name__,
238-
self._path
235+
self.path
239236
)
240237

241238
def _save(self, lines : [str]):
242-
with self._open(self._path, 'w') as cf:
239+
with self._open(self.path, 'w') as cf:
243240
for l in lines:
244241
cf.write(l)
245242

246243
def __delitem__(self, k):
247-
with self._open(self._path) as cf:
244+
with self._open(self.path) as cf:
248245
lines = alter_config({k : None}, cf)
249246
self._save()
250247

251248
def __getitem__(self, k):
252249
return read_config(
253-
self._open(self._path),
250+
self._open(self.path),
254251
selector = k.__eq__
255252
)[k]
256253

@@ -282,39 +279,26 @@ def __exit__(self, exc, val, tb):
282279
self._restored.clear()
283280
return exc is None
284281

285-
def path():
286-
def fget(self):
287-
return pg_str.split_ident(self["search_path"])
288-
def fset(self, value):
289-
self['search_path'] = ','.join([
290-
'"%s"' %(x.replace('"', '""'),) for x in value
291-
])
292-
def fdel(self):
293-
self['search_path'] = None
294-
doc = 'structured search_path interface'
295-
return locals()
296-
path = property(**path())
297-
298282
def get(self, k, alt = None):
299283
return read_config(
300-
self._open(self._path), selector = k.__eq__
284+
self._open(self.path), selector = k.__eq__
301285
).get(k, alt)
302286

303287
def keys(self):
304-
return read_config(self._open(self._path)).keys()
288+
return read_config(self._open(self.path)).keys()
305289

306290
def values(self):
307-
return read_config(self._open(self._path)).values()
291+
return read_config(self._open(self.path)).values()
308292

309293
def items(self):
310-
return read_config(self._open(self._path)).items()
294+
return read_config(self._open(self.path)).items()
311295

312296
def update(self, keyvals):
313297
"""
314298
Given a dictionary of settings, apply them to the cluster's
315299
postgresql.conf.
316300
"""
317-
with self._open(self._path) as cf:
301+
with self._open(self.path) as cf:
318302
lines = alter_config(keyvals, cf)
319303
self._save(lines)
320304

@@ -325,7 +309,7 @@ def getset(self, keys):
325309
"""
326310
keys = set(keys)
327311
cfg = read_config(
328-
self._open(self._path),
312+
self._open(self.path),
329313
selector = keys.__contains__
330314
)
331315
for x in (keys - set(cfg.keys())):

postgresql/driver/dbapi20.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,9 @@ def close(self):
285285
if self.database.closed:
286286
err = Error(
287287
"connection already closed",
288-
source = 'DRIVER',
289-
)
290-
self.database.ife_descend(err)
291-
err.raise_exception()
288+
source = 'CLIENT',
289+
creator = self.database
290+
).raise_exception()
292291
self.database.close()
293292

294293
def cursor(self):
@@ -298,13 +297,12 @@ def commit(self):
298297
if self._xact is None:
299298
err = InterfaceError(
300299
"commit on connection in autocommit mode",
301-
source = 'DRIVER',
300+
source = 'CLIENT',
302301
details = {
303302
'hint': 'The "autocommit" property on the connection was set to True.'
304-
}
305-
)
306-
self.database.ife_descend(err)
307-
err.raise_exception()
303+
},
304+
creator = self.database
305+
).raise_exception()
308306
self._xact.commit()
309307
self._xact = self.database.xact()
310308
self._xact.start()
@@ -316,10 +314,9 @@ def rollback(self):
316314
source = 'DRIVER',
317315
details = {
318316
'hint': 'The "autocommit" property on the connection was set to True.'
319-
}
320-
)
321-
self.database.ife_descend(err)
322-
err.raise_exception()
317+
},
318+
creator = self.database
319+
).raise_exception()
323320
self._xact.rollback()
324321
self._xact = self.database.xact()
325322
self._xact.start()

0 commit comments

Comments
 (0)