On 07/15/2016 07:49 AM, Alex Grönholm wrote:
The documentation provides the following example snippet for using
sessions within a context manager:

so, back when I started putting "examples" in those docs, the idea was like, "hey, here's an *example*. The Python programmer is free to do whatever they wish with these examples, and adjust as necessary".

That is, the reason something is an example and not a feature is, "you don't have to do it this way! do it however you want".

That there's been a trend recently of examples being used as is, but then when the example lacks some feature they result in bug reports against the library itself (not this case, but a different case recently comes to mind), is sadly the opposite of what i had intended. Of course examples can be modified to be reasonable, however.



@contextmanager
def session_scope():
    """Provide a transactional scope around a series of operations."""
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

I've been wondering why there is an except: block there. Shouldn't
session.close() be enough? At least according to the documentation, the
active transaction is rolled back by default when the connection is
returned to the pool.


that is correct. However .close() does not reset the state of the objects managed by the Session to be "expired", which arguably is necessary because without the transaction, you now have no idea what the state of the object's corresponding rows in the database are (this is what the whole "SQLAlchemy Session: In Depth" talk is about).

In reality, the above context manager is probably not that useful because it bundles the lifespan of the Session and the lifespan of a transaction together, and IMO an application should be more thoughtful than that.


This snippet has a second potential problem: what if the transaction is
in a bad state when exiting the block? Shouldn't session.commit() be
skipped then?

it's assumed that if anything is in "a bad state" then an exception would have been raised, you'd not reach commit().

Otherwise, if the idea is, "I'm using this context manager, but I'm not sure I want to commit at the end even though nothing was raised", well then this is not the context manager for you :). The example of contextmanagers for things like writing files and such sets up the convention of, "open resource, flush out all changes at the end if no exceptions". That's what people usually want.


Like, if not session.is_active: session.commit()? Let's
say the user code catches IntegrityError but doesn't roll back.

if it doesn't re-raise, then we'd hit the commit() and that would probably fail also (depending on backend). I don't see how that's different from:

with open("important_file.txt", "w") as handle:
    handle.write("important thing #1")
    handle.write("important thing #2")
    try:
         important_thing_number_three = calculate_special_thing()
         handle.write(important_thing_number_three)
    except TerribleException:
         log.info("oh crap! someone should fix this someday.")
    handle.write("important thing #4")





The
example code will then raise an exception when it tries to commit the
session transaction. Am I missing something?

On the better backends like Postgresql, it would.

If there's a use case you're looking for here, e.g. catch an IntegrityError but not leave the transaction, that's what savepoints are for. There should be examples there.


Now, if someone on IRC is using savepoints with IntegrityError and the context manager above and they're on Python 2 and are using MySQL and getting deadlock errors and can't see the original cause, there's a very specific sad situation going on with that which is https://bitbucket.org/zzzeek/sqlalchemy/issues/2696 and I can help them with that. But that's using the built in context managers. Using your own context manager is a great way to get around that bug :).




--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to sqlalchemy+unsubscr...@googlegroups.com
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto:sqlalchemy@googlegroups.com>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to