6

I have a Sanic application, and want to retrieve app.config from a blueprint as it holds MONGO_URL, and I will pass it to a repository class from the blueprint.

However, I could not find how to get app.config in a blueprint. I have also checked Flask solutions, but they are not applicable to Sanic.

My app.py:

from sanic import Sanic
from routes.authentication import auth_route
from routes.user import user_route

app = Sanic(__name__)
app.blueprint(auth_route, url_prefix="/auth")
app.blueprint(user_route, url_prefix="/user")

app.config.from_envvar('TWEETBOX_CONFIG')
app.run(host='127.0.0.1', port=8000, debug=True)

My auth blueprint:

import jwt
from sanic import Blueprint
from sanic.response import json, redirect
from domain.user import User
from repository.user_repository import UserRepository
...

auth_route = Blueprint('authentication')
mongo_url = ?????
user_repository = UserRepository(mongo_url)
...

@auth_route.route('/signin')
async def redirect_user(request):
    ...
davidism
  • 121,510
  • 29
  • 395
  • 339
skynyrd
  • 942
  • 4
  • 14
  • 34

4 Answers4

11

The Sanic way...

Inside a view method, you can access the app instance from the request object. And, therefore access your configuration.

@auth_route.route('/signin')
async def redirect_user(request):
    configuration = request.app.config

2021-10-10 Update

There are two newer ways to get to the configuration values (or, perhaps more accuratlely getting the application instance from which you can get the configuration). The first version might be more on point to answering the question of how to get to the config from the blueprint. However, the second option is probably the preferred method since it is precisely intended for this kind of use.

Alternative #1

Blueprints have access to the Sanic applications they are attached to beginning with v21.3.

Therefore, if you have a blueprint object, you can trace that back to the application instance, and therefore also the config value.

app = Sanic("MyApp")
bp = Blueprint("MyBlueprint")

app.blueprint(bp)

assert bp.apps[0] is app

The Blueprint.apps property is a set because it is possible to attach a single blueprint to multiple applications.

Alternative #2

Sanic has a built-in method for retrieving an application instance from the global scope beginning in v20.12. This means that once an application has been instantiated, you can retrieve it using: Sanic.get_app().

app = Sanic("MyApp")

assert Sanic.get_app() is app

This method will only work if there is a single Sanic instance available. If you have multiple application instances, you will need to use the optional name argument:

app1 = Sanic("MyApp")
app2 = Sanic("MyOtherApp")

assert Sanic.get_app("MyApp") is app1
Adam Hopkins
  • 6,837
  • 6
  • 32
  • 52
2

I would suggest a slightly different approach, based on the 12 Factor App (very interesting read which, among others, provides a nice guideline on how to protect and isolate your sensitive info).

The general idea is to place your sensitive and configuration variables in a file that is going to be gitignored and therefore will only be available locally.

I will try to present the method I tend to use in order to be as close as possible to the 12 Factor guidelines:

  1. Create a .env file with your project variables in it:

    MONGO_URL=http://no_peeking_this_is_secret:port/
    SENSITIVE_PASSWORD=for_your_eyes_only
    CONFIG_OPTION_1=config_this
    DEBUG=True
    ...
    
  2. (Important) Add .env and .env.* on your .gitignore file, thus protecting your sensitive info from been uploaded to GitHub.

  3. Create an env.example (be careful not to name it with a . in the beginning, because it will get ignored).
    In that file, you can put an example of the expected configuration in order to be reproducible by simply copy, paste, rename to .env.

  4. In a file named settings.py, use decouple.config to read your config file into variables:

    from decouple import config
    
    
    MONGO_URL = config('MONGO_URL')
    CONFIG_OPTION_1 = config('CONFIG_OPTION_1', default='')
    DEBUG = config('DEBUG', cast=bool, default=True)
    ...
    
  5. Now you can use these variables wherever is necessary for your implementation:

    myblueprint.py:

    import settings
    
    ...
    auth_route = Blueprint('authentication')
    mongo_url = settings.MONGO_URL
    user_repository = UserRepository(mongo_url)
    ... 
    

As a finisher, I would like to point out that this method is framework (and even language) agnostic so you can use it on Sanic as well as Flask and everywhere you need it!

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
  • 1
    While not a bad answer, it sort of ignores the question and skirts the actual implantation Danica has for this very issue. – Adam Hopkins Jan 24 '18 at 01:43
  • @TheBrewmaster That is mostly true (I am not directly solving the immediate problem), but this will solve this issue and probably some other considering the configuration process. It will allow the OP to store his/her `MONGO_URL` in a way that will not end up uploading secure info to git. – John Moutafis Jan 24 '18 at 07:54
  • This is a good way to access the base config, but it shouldn't then be used elsewhere; I'd set up the config like this, but then use the approach described by @TheBrewmaster below to access that config from within the blueprint. – El Yobo Feb 21 '19 at 05:12
  • 1
    @ElYobo As is mentioned at the end of this answer, this is a framework agnostic method that follows the guidelines of the 12factor app (one can use the same method even in a Flask/Sanic/Falcon/Django etc. app for example and have the same result). So I am not saying that Brewmaster's answer is wrong (far from it, it works as it should and I have upvoted it), I am just adding a universal way to do it as of the time this answer was written of course :) – John Moutafis Feb 21 '19 at 10:18
  • Yes, that makes sense and you do note that, however your last example in step 5 _is_ framework specific and it shows using it in a way which it specifically should _not_ be done with Sanic (accessing the config directly in the blueprint). Perhaps you could update the framework specific portion to show allocating it to the main app config instead? – El Yobo Feb 21 '19 at 22:47
  • @ElYobo I am not accessing the Sanic config, but a `settings.py` file on step 5. That is the role of such a file, to be accessed from anywhere. – John Moutafis Feb 22 '19 at 08:34
  • @JohnMoutafis, what's in the config isn't important - the sanic config is just a dict that you can put anything in to. It's bad practice to access global state from everywhere (as in your example) - passing it in to the functions as local state (as in The Bremaster's example) is better practice and makes it far easier to mock for tests etc. – El Yobo Feb 24 '19 at 03:31
0

There is a variable named current_app in Flask. You can use current_app.config["MONGO_URL"].
But I am not familiar with Sanic.

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
stamaimer
  • 6,227
  • 5
  • 34
  • 55
  • 1
    I saw there is a parameter named `request` which is a Request object of Sanic. And there is a attribute named `app` which is a reference to the Sanic application object that is handling this request. – stamaimer Apr 13 '17 at 07:58
0

I think you can create a config.py to save your configuration, just like

config.py

config = {
    'MONGO_URL':'127.0.0.1:27017'
}

and use it in app.py

from config import config

mongo_url = config['MONGO_URL']
Rotareti
  • 49,483
  • 23
  • 112
  • 108
Potato Running
  • 408
  • 4
  • 11
  • Thanks for your reply, however I don't want to put my critical information into a python code as passwords and other information should be seperated and located in a something like .config file for continuous integration and security. – skynyrd Apr 15 '17 at 15:51