def serve_eggs_with_spam(): box = picobox.Box() # on requests to /eggs, override the value of magic with 'spam' if request.path == "/eggs": box.put("magic", "spam") picobox.push(box, chain=True)
def main(args=sys.argv[1:]): # write access/error logs to stderr logging.basicConfig() logging.getLogger('aiohttp').setLevel(logging.INFO) conf = get_conf() database = create_connection(conf) # The secret is required to sign issued JWT tokens, therefore it's crucial # to warn about importance of settings secret before going production. The # only reason why we don't enforce it's setting is because we want the app # to fly (at least for development purpose) using defaults. if not conf.get('AUTH_SECRET', ''): warnings.warn( 'Auth secret has not been provided. Please generate a long random ' 'secret before going to production.') conf['AUTH_SECRET'] = binascii.hexlify(os.urandom(32)).decode('ascii') with picobox.push(picobox.Box()) as box: box.put('conf', conf) box.put('database', database) web.run_app( create_app(conf, database), host=conf['SERVER_HOST'], port=conf['SERVER_PORT'], access_log_format=conf['SERVER_ACCESS_LOG_FORMAT'], )
async def testapp(request, aiohttp_client, testconf, testdatabase): box = picobox.Box() box.put('conf', testconf) box.put('database', testdatabase) picobox.push(box) # Python 3.5 does not support yield statement inside coroutines, hence we # cannot use yield fixtures here and are forced to use finalizers instead. # This is especially weird as Picobox provides convenient context manager # and no plain functions, that's why manual triggering is required. request.addfinalizer(lambda: picobox.pop()) return await aiohttp_client( create_app(), # If 'Content-Type' is not passed to HTTP request, aiohttp client will # report 'Content-Type: text/plain' to server. This is completely # ridiculous because in case of RESTful API this is completely wrong # and APIs usually have their own defaults. So turn off this feature, # and do not set 'Content-Type' for us if it wasn't passed. skip_auto_headers={'Content-Type'}, )
import picobox @picobox.pass_("conf") def session(conf): class Session: connection = conf["connection"] return Session() @picobox.pass_("session") def compute(session): print(session.connection) box = picobox.Box() box.put("conf", {"connection": "sqlite://"}) box.put("session", factory=session) with picobox.push(box): compute()
import picobox def spam(): return eggs() def eggs(): return rice() @picobox.pass_("secret") def rice(secret): print(secret) with picobox.push(picobox.Box()) as box: box.put("secret", 42) # We don't need to propagate a secret down to rice which is good because # we kept interface clear (i.e. no changes in spam and eggs signatures). spam() # The other good thing is despite injection rice can explicitly receive # a secret which means its signature wasn't changed either. rice(13)
import picobox box = picobox.Box() box.put("magic", 12) # The app instance exposed via this module is used to run the app for # development (flask run) as well as for production (uWSGI, Gunicorn). # Therefore, we need to push a box without popping so it will be used # no matter which way we want to run the app. # # Examples: # # $ FLASK_APP=wsgi.py flask run # $ gunicorn wsgi:app # # Alternatively, one can push and pop the box before and after the # request (see Flask request context), but this way it would be harder # to test the app since any attempt to override dependencies in tests # will fail due to later attempt to push a new box by request hooks. picobox.push(box) from example import app # noqa