On Jun 29, 6:46 am, Michael Bayer <[email protected]> wrote:
> On Jun 28, 2011, at 9:34 PM, Ben Sizer wrote:
>
> > What does the "None None" signify? Would it be possible to change this
> > exception to be a bit more descriptive and a little less cryptic? eg.
> > Include the URI that failed?
>
> That error is raised by SQLite and we just propagate it out.  

Ok, that's a bit annoying, but it does sound like something that is
not your problem to solve. Maybe if I get time I'll contact the
pysqlite maintainers as I think it would be useful to have a pseudo-
statement in there at least.

> > Additionally, I notice that sqlalchemy doesn't attempt to make an
> > actual connection to the database until you perform the first query.
>
> That is true, all connections/transactions are lazy initializing.    Sorry 
> this was a surprise, though I don't think this is much of an issue once you 
> get used to dealing with lazy initializing objects.
>
> The formal pattern at play with SQLAlchemy's connection pool, Engine and 
> Session behavior is called the Proxy Pattern, a decent description is at 
> http://sourcemaking.com/design_patterns/proxy.

Yeah, that's all fine. I don't have a problem with this choice, just
that it's not immediately obvious when following the docs.

> the lazy initializing behavior of the Session is documented:
>
> http://www.sqlalchemy.org/docs/orm/session.html#unitofwork-transaction

Not exactly. It says it maintains a connection for each SQL statement,
which is not logically equivalent to saying there is no connection
unless there has been an SQL statement. There is a similar implication
in the 'What does the Session do?' paragraph also.

> But that doesn't have anything to do with the Engine, which is its own thing. 
>   As far as the Engine, the docs currently use the term "connect" in 
> conjunction with create_engine() which is for simplicities' sake, but is 
> technically inaccurate, perhaps come up with some term other than "connect", 
> "configure a connection source" perhaps.   We can add an explicit sentence to 
> the top ofhttp://www.sqlalchemy.org/docs/core/connections.html.

I must admit I don't fully understand engines, connections, sessions,
etc. I will study the docs further! However when writing manual DB
code I would usually have a step where I initialise the driver (which
I had been thinking of as the create_engine stage), a step where I can
issue statements (which I think of as the session stage), and a point
temporally between those where I explicitly connect to the physical
storage, which I had thought was automatically taking place when
creating the engine but I now realise only happens on demand. It's not
a problem as such, just a surprise.

> > I expect I am not unusual
> > in wanting an error in session configuration to fail as soon as
> > possible,
>
> Nobody has ever raised this issue before to my recollection.

The problem with show-stopper bugs is that people often just give up
and switch to something else rather than tell you about it. ;)

(Of course in this case, the bug is mine, but developers do like to
blame their tools...)

> I'm not sure lots of users are phased whether the stack trace starts at the 
> Session.configure() line or if it starts later as soon as their first unit 
> test tries to hit the database - they get the same error, see that the 
> connection URL is bad, and fix it.

But this is exactly the problem: I had one unit test for setting up
the database, and one for actually using the database. The first one
passed - the second one failed. So I immediately think, "my usage of
the database is wrong", not "I set up the database connection
wrongly", because all the code for setting up the connection was
covered the test that passed, too. That is what the unit tests were
there for, after all - to separate out where a problem appears to be
coming from, by splitting your code coverage across tests.

> I suppose inefficiency is the only issue but its so unnecessarily 
> inefficient, most people would consider it to be wrong behavior.    A Session 
> may be configured such that depending on what's requested of it, it can 
> connect to any number of different engines - connecting to 
> several/dozens/hundreds of engines unconditionally upon construction is not 
> an option.

Yeah, that's fine. I just think it would be good to have had something
about this documented right in at the top of the Session docs so that
anyone starting out or writing unit tests for this sort of thing knows
that they're not actually testing the db connection just because they
made an engine and Session without error. Anywhere you pass in invalid
data, you hope that the system catches that as soon as possible so
that the problem's narrowed down, ideally to the exact statement that
is incorrect. In this case, it's not efficient to do so, therefore it
would be great for the docs to explicitly highlight that fact.

> Feel free to suggest what verbiage you're looking for, it's not hitting me 
> strongly what the confusion is - the application failed to connect, you get 
> an error describing the problem, the stack trace shows it happened upon 
> connect() inside the pool.     An application should always have unit tests 
> which will reveal basic issues like this immediately.

As noted above, the nature of the lazy evaluation is such that you end
up not testing the functionality that you think you're testing, which
combined with the rather vague exception (not your fault, I know),
makes this a bit confusing to debug. I'm really not sure of the
precise wording I'd pick: there's a lot of stuff in "What does the
Session do?" and other parts of the Session docs which -hint- at there
being no connection until a query is issued, but which don't
explicitly spell that out.


> > (In fact, this part
> > of the docs is a bit confusing in general - there seem to be a fair
> > few permutations of how to approach it - pass arguments to
> > sessionmaker? or to Session.configure? or to Session's constructor? -
> > and it's not clear why they all have to exist.)
>
> They exist because the intended usage is that sessionmaker() be placed as a 
> global variable in the module space of an application - it is then often the 
> case that the actual Engine does not exist at this point, so cannot be passed 
> to the sessionmaker().   A later configuration step can then call configure() 
> to add new Engine configurations.    This usage is driven by the needs of web 
> frameworks and such.

Ok, so:
    Session = sessionmaker(bind=some_engine) is for when you know
which engine you need at import time;
    Session.configure(bind=engine) is when you have decided on the
engine and want all subsequent sessions to use that engine, without
needing to keep a reference to that engine;
    session = Session(bind=engine) is when you want to be able to
specify the engine on a per-session basis.

Is that right? It seems to me like the middle one isn't strictly
necessary as you could just keep a reference to the engine yourself
rather than injecting it into the Session, but I guess that's just a
personal preference.

I can see why you'd call sessionmaker() at file scope to ensure it's
done at import time, but then why isn't this part just rolled into the
sqlalchemy package directly? Can't it just expose Session to me and
let me call .configure on it? Maybe that's exactly what it means when
the docs say, "Session is a regular Python class which can be directly
instantiated.", which is great, but that means there would be 4 ways
of approaching this, which seems a little much. It would be great if
the Session docs gave a short sentence on the rationale behind each of
these use cases so that a new user can know the implications of the
one he or she picks. At the moment it's sort of assumed that you will
know why you want one or the other, without the differences being
clear.

> The Session constructor itself is not mentioned there - when you see it say 
> Session(), that's an invocation of the sessionmaker() function.

The docs say, "the sessionmaker() call creates a class for us, which
we assign to the name Session". So when you call "session =
Session(bind=conn)", you're invoking the Session constructor/__init__
method, surely? (I appreciate that the Session object here might not
be the same as sqlalchemy.orm.session.Session, whatever that is, but
from the user's perspective, there is a class called Session which
we're instantiating.)

Thanks for your help so far.

--
Ben Sizer

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to