On Jun 28, 2011, at 9:34 PM, Ben Sizer wrote:
> Hello all,
>
> When using sqlalchemy 0.7 with sqlite, if I enter the path URI
> incorrectly I will get an error like this:
>
>OperationalError: (OperationalError) unable to open database file
> None None
>
> 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. DBAPI does not
have any standardized error codes and there's no clean way for us to "wrap" the
exception into something new without concealing what the original exception was
- we "wrap" it in a sqlalchemy.exc.DBAPIError so that an application can catch
DBAPI exceptions in a generic way (otherwise you'd have to import
sqlite3.OperationalError), but other than that we don't mess with it.Python
3 allows exceptions to be "chained" but Python 2 doesn't have a terrific way to
do it. The None None is part of DBAPIError's behavior, it illustrates the
statement and parameters which took place.We can consider not displaying
this if the statement is None, i agree it's not pleasing to the eye but nobody
has ever mentioned it before.
>
> Additionally, I notice that sqlalchemy doesn't attempt to make an
> actual connection to the database until you perform the first query.
> That means that code like this will appear to work:
>
>Session = sessionmaker()
>engine = create_engine('sqlite:///%s' % "invalid_filename")
>Base.metadata.bind = engine
>db_session = Session(bind=engine)
>
> Yet eventually, when you make a query within your app code, you'll get
> an exception because the URI was wrong. In my case, due to the
> exception text being a little vague, I thought this was a problem with
> my app's db access patterns (as I do things like delete the database
> file manually) when it was just a problem with the initial connection.
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 .
>
> I found a way to trigger this error earlier, by issuing
> engine.connect() in the above routine, but I notice that this isn't
> explicitly documented in "Using The Session" (http://
> www.sqlalchemy.org/docs/orm/session.html).
the lazy initializing behavior of the Session is documented:
http://www.sqlalchemy.org/docs/orm/session.html#unitofwork-transaction
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 of
http://www.sqlalchemy.org/docs/core/connections.html.
> 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. 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.
> so if it's not possible or efficient to do this
> automatically as part of creating the session,
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. Its usage is such that
once rollback or commit is called, it's essentially dormant, not using any
resources, until it's called upon again to do something. The lazy
initialization model makes the Session very easy to use as they are cheap to
create and only establish state with the database as needed, release it as soon
as it's not.It's a very user-friendly usage model, as long as one is
comfortable with lazy initialization.
> perhaps this part of
> the docs could be clarified so that new users in future will know
> exactly what to call to test this configuration?
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