Re: [sqlalchemy] Session and context manager

2018-02-05 Thread Mike Bayer
On Mon, Feb 5, 2018 at 8:15 AM, Jacek Blocki  wrote:
> Mike,
> Thank you for for the tip on _sessions, I think it is worth publishing since
> it can make debugging easier. Regarding documentation it will be good to
> mention session close() is not like file close() and closed session is just
> ready for another transaction. Context manager is typically introduced with
> file operations, so python beginner may be left with false assumption closed
> session is gone and there is no need to bother with it.

could use more detail I see but if you .close() a Session it's safe to discard.

> Separate and external session life cycle perhaps also needs another wording.
> SQA user must be aware what session are defined and from which session does
> the object come. Objects like queries have information on session
> internally, so "separate and external management of session life cycle"
> looks impossible for them.

you *maintain the start/endpoints* of the session separately and
externally but you still make it available to methods that need to use
it. A method that works with objects only works with one session
at a time so they always know from what session the object came.
it's a large subject of general application design but the examples
try to steer it that way.   if someone doesn't know how to program
that way though, it's going to be an uphill climb, they should look at
example applications.



> BTW are queries the only objects keeping a
> reference to session after close()?

the Query is a short-term object created from a Session and you
shouldn't in the usual case be passing those around to things that
last outside of the scope of the Session.  You should use a query, get
your results, then throw it away.


> I've spent quite some time trying to
> understand why do I get  already attached to session error while all
> operations were handled with context manager.

"attachment" refers to objects loaded by a session, not the Query
object itself.   when you call session.close(), those objects are
de-associated.  you can't get this error unless you start using that
Session again (which includes if you use a Query that was created from
that Session), and then try to use those newly loaded objects
elsewhere.


In my application GUI I was
> using a query from one session to build dropdown selection and then tried to
> modify object attribute with dropdown selected object in another session.
> Code demonstrating thy blunder is attached below.
> Kind Regards,
> Jacek
>
 from sqlalchemy.orm.session import _sessions
 from sqlalchemy import inspect as insp

 #create a session
> ...
 ses=Session()
 # it is empty of course
> ...
 for i in ses: print i
> ...
 # query some data
> ...
 qr=ses.query(MeterReading)
 qr.count()
> 43
 43
> 43
 # session is still empty - no object referenced yet
> ...
 for i in ses: print i
> ...
 r0 = qr[0]
 # now we have one object in ses
> ...
 for i in ses: print i
> ...
> 
 # close will empty ses
> ...
 ses.close()
 for i in ses: print i
> ...
 # let's take a picture before we say farewell
> ...
 ses
> 
 insp(r0).session
 # replace old ses with brand new from the factory
> ...
 ses = Session()
 ses.add(r0)
 # take another object form query qr and add it to session - it will
 throw an exception
> ...
 r1 = qr[1]

qr is a Query object.   You've closed the Session to which qr is
attached but kept qr around, so you are emitting SQL here, using the
Session and putting new objects into it.  "r1" is now associated with
the session you previously closed.

 ses.add(r1)
> Traceback (most recent call last):
> [...]
> sqlalchemy.exc.InvalidRequestError: Object ' 0x7f2b166065d0>' is already attached to session '2' (this is '3')
 # because old ses is still there, qr is associated with it and so is r1

as expected


> ...
 for i in _sessions.items(): print i
> ...
> (1, )
> (2, )
> (3, )
 insp(r0).session
> 
 insp(r1).session
> 
 insp(qr).session
> 

 # what if MeterReading has an atribute meter which is a realtionship
> ... insp(MeterReading.meter).mapper.class_
> 

 # suppose we want to modify r0.meter
> ...
 qm = insp(r1).session.query(Meter)
 qm.count()
> 3
 # r0.meter modification with qm will fail
> ...
 r0.meter = qm[1]
> Traceback (most recent call last):
> [...]
> sqlalchemy.exc.InvalidRequestError: Object '' is
> already attached to session '2' (this is '3')
 # obviously it adds meter to r0 session, so we need to disconnect it
 form current session
> ... m1 = qm[1]
 insp(m1).session.expunge(m1)
 r0.meter = m1
 # now it works,
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You receive

Re: [sqlalchemy] Session and context manager

2018-02-05 Thread Jacek Blocki
Mike, 
Thank you for for the tip on _sessions, I think it is worth publishing 
since it can make debugging easier. Regarding documentation it will be good 
to mention session close() is not like file close() and closed session is 
just ready for another transaction. Context manager is typically introduced 
with file operations, so python beginner may be left with false assumption 
closed session is gone and there is no need to bother with it. 
Separate and external session life cycle perhaps also needs another 
wording. SQA user must be aware what session are defined and from which 
session does the object come. Objects like queries have information on 
session internally, so "separate and external management of session life 
cycle" looks impossible for them. BTW are queries the only objects keeping 
a reference to session after close()? I've spent quite some time trying to 
understand why do I get  already attached to session error while all 
operations were handled with context manager. In my application GUI I was 
using a query from one session to build dropdown selection and then tried 
to modify object attribute with dropdown selected object in another 
session. Code demonstrating thy blunder is attached below. 
Kind Regards,
Jacek

>>> from sqlalchemy.orm.session import _sessions
>>> from sqlalchemy import inspect as insp
>>> 
>>> #create a session
... 
>>> ses=Session()
>>> # it is empty of course
... 
>>> for i in ses: print i 
... 
>>> # query some data
... 
>>> qr=ses.query(MeterReading)
>>> qr.count()
43
>>> 43
43
>>> # session is still empty - no object referenced yet
... 
>>> for i in ses: print i
... 
>>> r0 = qr[0]
>>> # now we have one object in ses
... 
>>> for i in ses: print i
... 

>>> # close will empty ses
... 
>>> ses.close()
>>> for i in ses: print i
... 
>>> # let's take a picture before we say farewell 
... 
>>> ses

>>> insp(r0).session
>>> # replace old ses with brand new from the factory
... 
>>> ses = Session()
>>> ses.add(r0)
>>> # take another object form query qr and add it to session - it will 
throw an exception
... 
>>> r1 = qr[1]
>>> ses.add(r1)
Traceback (most recent call last):
[...]
sqlalchemy.exc.InvalidRequestError: Object '' is already attached to session '2' (this is '3')
>>> # because old ses is still there, qr is associated with it and so is r1
... 
>>> for i in _sessions.items(): print i
... 
(1, )
(2, )
(3, )
>>> insp(r0).session

>>> insp(r1).session

>>> insp(qr).session

>>> 
>>> # what if MeterReading has an atribute meter which is a realtionship 
... insp(MeterReading.meter).mapper.class_

>>> 
>>> # suppose we want to modify r0.meter
... 
>>> qm = insp(r1).session.query(Meter)
>>> qm.count()
3
>>> # r0.meter modification with qm will fail
... 
>>> r0.meter = qm[1]
Traceback (most recent call last):
[...]
sqlalchemy.exc.InvalidRequestError: Object '' is 
already attached to session '2' (this is '3')
>>> # obviously it adds meter to r0 session, so we need to disconnect it 
form current session
... m1 = qm[1]
>>> insp(m1).session.expunge(m1)
>>> r0.meter = m1
>>> # now it works, 

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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.


Re: [sqlalchemy] Session and context manager

2018-01-31 Thread Mike Bayer
On Wed, Jan 31, 2018 at 8:37 AM, Jacek Blocki  wrote:
> In SQLAlchemy documentation there is an example of handling session with
> context manager:
>
> from contextlib import contextmanager
>
> @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()
>
>
> What are real benefits of creating new session vs. returning the same
> session kept at module level in above approach?

well you wouldn't want to re-use a module-level session except in a
small console application, because such a session isn't threadsafe and
"module level" usually implies "global variable".


> Some objects like query will
> keep session reference even after session.close(), so created session is not
> guaranteed to end with contextmanager call.

this is true, session.close() does not end the life of the session,
you can keep reusing it.  close() just empties it out of all objects
and transactional state.  this does not imply any failure mode.

> Then it may lead to
> 'sqlalchemy.exc.InvalidRequestError: is already attached to session'
> exceptions.

not true, because when you call close(), all objects are de-associated
from that session.


> Of course in the docs there is a warning above approach is for
> "advanced developers", who can sort out any issue or risk their advanced
> badge revoked...

oh.  the paragraph about the context manager uses the phrase "the
advanced developer", that seems to be misleading.  That's not the
usual "Warning: this method is for advanced developers" type of
warning. it says that with the intent of "don't worry if you don't
understand what @contextmanager is yet".   I'll modify that language
now as it has failed.That will be up on the site within the hour.


> Is there any way to list all sqlalchemy session objects? It may be useful id
> debugging.

ah, well there is though it's not public, but feel free to use it, if
you think it's useful it can be provided as a public method:

from sqlalchemy.orm import session

print(session._sessions.values())

session._sessions is a WeakValueDictionary.

> Regards, Jacek
>
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> 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.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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.