On Tue, 17 Jan 2017 05:06 am, Israel Brewster wrote: > I generally use context managers for my SQL database connections, so I can > just write code like: > > with psql_cursor() as cursor: > <do whatever> > > And the context manager takes care of making a connection (or getting a > connection from a pool, more likely), and cleaning up after the fact (such > as putting the connection back in the pool), even if something goes wrong. > Simple, elegant, and works well. > > The problem is that, from time to time, I can't get a connection, the > result being that cursor is None,
Seriously? psql_cursor().__enter__ returns None instead of failing? That sounds like a poor design to me. Where is this psql_cursor coming from? > and attempting to use it results in an > AttributeError. So my instinctive reaction is to wrap the potentially > offending code in a try block, such that if I get that AttributeError I > can decide how I want to handle the "no connection" case. This, of course, > results in code like: > > try: > with psql_cursor() as cursor: > <do whatever> > except AttributeError as e: > <handle no-connection case> Except that isn't necessarily the no-connection case. It could be *any* AttributeError anywhere in the entire with block. > I could also wrap the code within the context manager in an if block > checking for if cursor is not None, but while perhaps a bit clearer as to > the purpose, now I've got an extra check that will not be needed most of > the time (albeit a quite inexpensive check). It's cheap, it's only needed once (at the start of the block), it isn't subject to capturing the wrong exception... I would definitely write: with psql_cursor() as cursor: if cursor is not None: <do whatever> > The difficulty I have with either of these solutions, however, is that > they feel ugly to me - and wrapping the context manager in a try block > almost seems to defeat the purpose of the context manager in the first > place - If I'm going to be catching errors anyway, why not just do the > cleanup there rather than hiding it in the context manager? Context managers don't necessarily swallow exceptions (although they can). That's not what they're for. Context managers are intended to avoid: try: ... finally: ... *not* try...except blocks. If you need a try...except, then you could avoid using the context manager and re-invent the wheel: try: ... except: ... finally: # do whatever cleanup the context manager already defines # but now you have to do it yourself or you can let the context manager do what it does, and write your own code to do what you do: try: with ...: ... except: ... [...] > says "there should be a better way", so I figured I'd ask: *is* there a > better way? Perhaps some way I could handle the error internally to the > context manager, such that it just dumps me back out? That's what's supposed to happen: py> with open('foobarbaz') as f: ... pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: 'foobarbaz' Notice that the context manager simply raises an exception on failure, which I can catch or not as I so choose, rather than returning None. I really think that the problem here is the design of psql_cursor(). > Of course, that > might not work, given that I may need to do something different *after* > the context manager, depending on if I was able to get a connection, but > it's a thought. Options? -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list