I have a Flask project where the entry point is application.py and then I have several other modules like, e.g. variant.py, etc.
The project structure is:
>my_app_dir/
application.py
views/
__init__.py
users.py
variant.py
...
For variant.py, it's a function like:
import ...
from views import *
def variant(variant_id, subset='all', language='en'):
...
if subset == 'all':
return json.dumps(x)
return json.dumps([{subset: y[subset]} for y in x])
The point is I want to use variant.py like an API, so I am testing via iPython, something like, but it's returning an error:
from views import variant as v
aa = v.variant('22-38212762-A-G')
...
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
I've tried googling but couldn't find any similar case, yet I experimented several things for no avail.
In the end, I found out a way to get what I was looking for:
from views import application, autocomplete
from views.variant import variant
ctx = application.test_request_context(path='/login',method='POST', data={'user':'demo','password':'demo123'})
ctx.push()
variant('22-38212762-A-G')[:50]
autocomplete.autocomplete('ttll','gene').json
So, essentially, the trick bit is:
ctx = application.test_request_context(path='/login',method='POST', data={'user':'demo','password':'demo123'})
ctx.push()
Related
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):
...
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
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:
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
...
(Important) Add .env and .env.* on your .gitignore file, thus protecting your sensitive info from been uploaded to GitHub.
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.
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)
...
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!
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']
There is a variable named current_app in Flask. You can use current_app.config["MONGO_URL"].
But I am not familiar with Sanic.
first off, a disclaimer: I'm not well versed in python or flask, so bear with me.
I'm trying to put together a minimal API using flask, i was planning to dynamically generate routes and their associated procs from the contents of a subdirectory.
The code looks something like this:
from flask import Flask
import os
app = Flask(__name__)
configs = os.getcwd() + "/configs"
for i in os.listdir(configs):
if i.endswith(".json"):
call = "/" + os.path.splitext(i)[0]
#app.route(call, methods=['POST'])
def call():
return jsonify({"status": call + "Success"}), 200
The plan being to iterate over a bunch of config files and use their naes to define the routes. Now, this works for a single config file, but wont work for multiple files as I end up trying to overwrite the function call that is used by each route.
I can factor out most of the code to a separate function as long as i can pass in the call name. However it seems that however i go about this i need to dynamically name the function generated and mapped to the route.
So, my question is: how can use the contents of a variable, such as 'call' to be the function name?
i.e. something like
call = "getinfo"
def call(): # Effectively being evaled as def getinfo():
Everything i've tried hasn't worked, and i'm not confident enough in my python syntax to know if it's because i'm just doing something silly.
Alternatively is there another way to do what i'm trying to achieve?
Thanks for all and any feedback!
Thanks for the help. I've moved to one route and one handler and building up the file list, and handling of the request paths, etc separately.
This is a sanitized version of the model i now have:
from flask import Flask
import os
calls = []
cfgs = {}
app = Flask(__name__)
configs = os.getcwd() + "/configs"
for i in os.listdir(configs):
if i.endswith(".json"):
cfgs[call] = os.path.splitext(i)[0]
calls.extend([call])
#app.route('/<call>', methods=['POST'])
def do(call):
if call not in calls:
abort(400, "invalid call")
# Do stuff
return jsonify({"status": call + "Success"}), 200
if __name__ == '__main__':
app.run(debug=True)
So, thanks to the above comments this is doing what i'm after. Still curious to know if there is any way to use variables in function names?
I'm using werkzeug in a Django project using Apache/mod_wsgi. What I want to do is access the werkzeug python shell without there actually being an error. The only way I can figure to do this is to intentionally cause an error when the url pattern url(r'^admin/shell', forceAnError()) is matched.
Admittedly, intentionally causing an error isn't the optimal course of action, so if there's a way to simply call/import/render/access the werkzeug python shell from a template or something, that would be the better solution.
If you wrap your WSGI application in a werkzeug.debug.DebuggedApplication with evalex on, you'll get a shell available at /console:
from werkzeug.wrappers import Request, Response
from werkzeug.debug import DebuggedApplication
#Request.application
def app(request):
return Response("Normal application, nothing to see here...")
app = DebuggedApplication(app, evalex=True)
# console_path is another optional keyword argument.
# you can guess what it does.
My python GAE app's central application file looks like this:
import webapp2
import homepage
import user_auth
import user_confirm
import admin_user
import admin_config
import config
app = webapp2.WSGIApplication([
(user_auth.get_login_url(), user_auth.LoginHandler),
(user_auth.get_logout_url(), user_auth.LogoutHandler),
("/user/confirm", user_confirm.UserConfirmHandler),
("/admin/config", admin_config.AdminConfigHandler),
("/admin/user/add", admin_user.AdminAddUserHandler),
("/admin/user", admin_user.AdminUserHandler),
("/", homepage.HomepageHandler),
], debug=True)
As you can see, I must import a bunch of request handlers, but for each request, only one of them is used, the other imports are just useless!
That's a big waste of memory and performance because those unnecessary imports also import other things on their own. Does Google App Engine have some "caching" mechanism or something that makes these unnecessary imports negligible? I think not.
How can I avoid them? I just haven't found out the way to import 1 Request Handler per request. If I put all the routing to app.yaml, that would work the way I want, but it makes things complex because I must write app = webapp2.WSGIApplication(... for every request handler file and repeat those boring urls twice (both in the python file and in app.yaml).
Found the way here, already built into webapp2
http://webapp-improved.appspot.com/guide/routing.html#lazy-handlers
Im playing around with web.py as a lightweight web framework. Im having problems when i attempt to move the actual implementation of my page into a separate file instead of the root file. As a demonstration, My core.py file looks like this:
import web, sys, os
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
urls = (
'/', 'index'
)
app = web.application(urls, globals())
render = web.template.render('templates/')
if __name__ == "__main__":
app.run()
ive moved my implementation into a file called index.py at the same level as core.py. My implementation looks like this:
class index:
def GET(self):
return "Hello world"
however, whenever i run my application, i get an error:
<type 'exceptions.KeyError'> at /
can anybody tell me what is going on?
According to http://webpy.org/tutorial3.en#urlhandling, web.py does a lookup for the classes you specified in your urls in the global namespace.
In your core.py there is no class named index (after you moved it), that's what causes this keyerror. In my test I could fix that by importing the index class in core.py.
from index import index
(I haven't used web.py before, so please correct me if I'm wrong)
You can add dots to crawl into modules. So say you have a folder controllers with a file named file.py and you wanted to access the controller named index:
from controllers import *
urls = (
'/', 'controllers.file.index'
)
I'm guessing the bug is in your template. I hit this error when if forgot a ':' on an if statement in my template.