Starting app.py, then killing the database and hitting /api/foo
gives me:
peewee.OperationalError: could not connect to server: Connection refused
Bringing the database back up gives me and hitting /api/foo
gives me:
peewee.OperationalError: terminating connection due to administrator command\nSSL connection has been closed unexpectedly\n
And hitting /api/foo
again gives me:
peewee.InterfaceError: connection already closed
Test case
test_case/__init__.py
#!/usr/bin/env python
from os import environ
from bottle import Bottle, request, response
from playhouse.db_url import connect
bottle_api = Bottle()
db = connect(environ['RDBMS_URI'])
from test_case.foo.models import Foo
db.connect() # Not needed, but do want to throw errors ASAP
db.create_tables([Foo], safe=True) # Create tables (if they don't exist)
from test_case.foo.routes import foo_api
bottle_api.merge(foo_api)
bottle_api.catchall = False
@bottle_api.hook('before_request')
def _connect_db():
print 'Connecting to db'
db.connect()
@bottle_api.hook('after_request')
def _close_db():
print 'Closing db'
if not db.is_closed():
db.close()
def error_catcher(environment, start_response):
try:
return bottle_api.wsgi(environment, start_response)
except Exception as e:
environment['PATH_INFO'] = '/api/error'
environment['api_error'] = e
return bottle_api.wsgi(environment, start_response)
@bottle_api.route('/api/error')
def global_error():
response.status = 500
return {'error': (lambda res: res[res.find("'") + 1:res.rfind("'")])(
str(request.environ['api_error'].__class__)),
'error_message': request.environ['api_error'].message}
test_case/__main__.py
from __init__ import bottle_api
# Or `from __init__ import bottle_api`; `from bottle import run`;
# Then `run(error_catcher, port=5555)`
bottle_api.run(port=5555)
test_case/foo/__init__.py
test_case/foo/models.py
from peewee import Model, CharField
from test_case import db
class Foo(Model):
id = CharField(primary_key=True)
class Meta(object):
database = db
test_case/foo/routes.py
from bottle import Bottle
from playhouse.shortcuts import model_to_dict
from test_case.foo.models import Foo
foo_api = Bottle()
@foo_api.get('/api/foo')
def retrieve_foos():
return {'foos': tuple(model_to_dict(foo) for foo in Foo.select())}
Update:
I believe the problem lies in how you've structured your imports and the way python loads and caches modules in sys.path.
I think that one of your modules is being imported and loaded twice and different parts of the codebase use different instances of the module.
Thus, the views in foo.routes, are using one instance of the database object, while the connection hooks are using another.
Instead of
from __init__
, what about tryingfrom test_case import bottle_api
? That is the one import statement that jumps out at me as a possible culprit.I added the following to your code so I could run it from the command-line:
Then I made a request to
/api/foo
and saw some fake data. I stopped the Postgresql server and got this error:When I restarted the server and made a subsequent request I got a normal response with my test data.
So, in short, I'm not sure what I may be missing but the code seems to be working correctly to me.
Postgresql 9.4, psycopg2 2.6, python 2.7.9, peewee 2.6.0