I am a newbie at Python Pyramid and working on improving an existing app that we have.
I have an app main function defined like below:
def web_main(global_config, **settings):
config = Configurator(settings=settings, root_factory=RootFactory)
...
...
config.add_request_method(get_user, "user", reify=True)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
...
app = config.make_wsgi_app()
return app
I want to override get_user request method with my implementation and also want to use my own authentication policy.
With that I was thinking to do write a function like below:
def my_web_main(global_config, **settings):
app = web_main(global_config, **settings)
<Set Overrides here>
return app
Inside the config.ini file I will call my_web_main to start this app.
I have not been able to figure out how to set the overrides. Would appreciate some inputs on this.
The configurator is where you should perform overrides. So the answer is to modify web_main or define your own. Pyramid has an override mechanism via config.include(), but it does not work one level higher where you're attempting to override things with a built wsgi-app. You have to do it at the config level.
Related
I am trying to use a global configuration when defining an ElasticSearch DSL model, which is more or less a regular Python class aka service class.
"""Define models"""
from elasticsearch_dsl import Document, Text
from flask import current_app
class Greeting(Document):
"""Define Greeting model"""
message = Text()
class Index:
name = current_app.config['GREETINGS_INDEX']
def save(self, ** kwargs):
return super().save(** kwargs)
Unfortunately, if my import statement is at the top of the view file, I get this error message:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
The only way to get things to work is if I import the model/service class inside the request like this:
from elasticsearch_dsl import Search
from flask import Blueprint, current_app
# from .models import Greeting ### this will throw the application context error
greetings = Blueprint(
'greetings',
__name__,
url_prefix='/greetings/'
)
...
#greetings.route("/elasticsearch/new/")
def new_greeting_using_elasticsearch():
from .models import Greeting ### this avoids the application context error
Greeting.init()
greeting = Greeting(message="hello, elastic")
greeting.save()
return(
"a greeting was saved; "
"it is viewable from https://localhost:5000/greetings/elasticsearch/"
)
This seems like a code smell. Is there another way to accomplish using reusing configurations that can keep the import statement at the top of the file?
These questions/answers seem to suggest that this is the only way:
How to access config value outside view function in flask
Flask - RuntimeError: Working outside of application context
Am I missing something? Should I rearchitect my application to avoid this? Or is this just a Flask-way/thing?
Thank you for your help 🙏
Other questions/answers/articles that did not help me:
"RuntimeError: Working outside of application context " with Python Flask app ( Sending gmail using scheduler )
https://flask.palletsprojects.com/en/0.12.x/appcontext/#creating-an-application-context
Access config values in Flask from other files
RuntimeError: working outside of application context
Python #property in Flask configs?
Reading properties from config file with Flask and Python
I created a flask addon using "flask fab create-addon".
I would like to change the template appbuilder/general/security/login_oauth.html so I have:
templates
appbuilder
general
security
login_oauth.html
But when I load the host application my version of login_oauth.html is not loaded. I tried registering a blueprint as in this post with the following code:
from flask import Blueprint
bp = Blueprint('fab_addon_fslogin', __name__, template_folder='templates')
class MyAddOnManager(BaseManager):
def __init__(self, appbuilder):
"""
Use the constructor to setup any config keys specific for your app.
"""
super(MyAddOnManager, self).__init__(appbuilder)
self.appbuilder.get_app.config.setdefault("MYADDON_KEY", "SOME VALUE")
self.appbuilder.register_blueprint(bp)
def register_views(self):
"""
This method is called by AppBuilder when initializing, use it to add you views
"""
pass
def pre_process(self):
pass
def post_process(self):
pass
But register_blueprint(bp) return:
File "/home/cquiros/data/projects2017/personal/software/superset/addons/fab_addon_fslogin/fab_addon_fslogin/manager.py", line 24, in __init__
self.appbuilder.register_blueprint(bp)
File "/home/cquiros/data/projects2017/personal/software/superset/env_superset/lib/python3.8/site-packages/Flask_AppBuilder-3.3.0-py3.8.egg/flask_appbuilder/base.py", line 643, in register_blueprint
baseview.create_blueprint(
AttributeError: 'Blueprint' object has no attribute 'create_blueprint'
Not much information out there on how to do this. Any clue is appreciated
If you want to customize login_oauth.html, The easiest way is that adding it into your app directly not addon.
That means the login_oauth.html should be put in this path.
YourApp
- app
-- templates
--- appbuilder
---- general
----- security
------ login_oauth.html
For solution of addons, the function self.appbuilder.register_blueprint is for views not for the object which Blueprint fuction returns. It should be replaced with
self.appbuilder.get_app.register_blueprint(Blueprint('fab_addon_fslogin', __name__, template_folder='templates'))
Keep the blueprint registration and
try to rename the login_oauth.html to login_oauth_xxxx.html which at
{your python packages root path}\site-packages\flask_appbuilder\templates\appbuilder\general\security
That will let the template be overwrote as you need. I guess that the template searching order of app-builder package is greater than addons. The searching order of blueprints depend on order of registrations
Finally, I have found a trick without renaming the file, you could try the following
class MyAddOnManager(BaseManager):
def __init__(self, appbuilder):
"""
Use the constructor to setup any config keys specific for your app.
"""
super(MyAddOnManager, self).__init__(appbuilder)
self.appbuilder.get_app.config.setdefault('MYADDON_KEY', 'SOME VALUE')
self.static_bp = Blueprint('fab_addon_fslogin', __name__, template_folder='templates')
def register_views(self):
"""
This method is called by AppBuilder when initializing, use it to add you views
"""
pass
def pre_process(self):
self.appbuilder.get_app.register_blueprint(self.static_bp)
blueprint_order = self.appbuilder.get_app._blueprint_order
# move blueprint order of addon to top
blueprint_order.insert(0, blueprint_order.pop())
def post_process(self):
pass
Reference:
flask-appbuilder Customizing
I've met a scenario which I have to override a common middleware in CKAN. And in CKAN default plugins/interface.py:
class IMiddleware(Interface):
u'''Hook into CKAN middleware stack
Note that methods on this interface will be called two times,
one for the Pylons stack and one for the Flask stack (eventually
there will be only the Flask stack).
'''
def make_middleware(self, app, config):
u'''Return an app configured with this middleware
When called on the Flask stack, this method will get the actual Flask
app so plugins wanting to install Flask extensions can do it like
this::
import ckan.plugins as p
from flask_mail import Mail
class MyPlugin(p.SingletonPlugin):
p.implements(p.I18nMiddleware)
def make_middleware(app, config):
mail = Mail(app)
return app
'''
return app
It shows that I have to define "MyMiddleWare" class under a plugin that I want to implement in an extension. However, as it shows in the example, the actual middleware Mail is imported from a different class. I want to override TrackingMiddleware especially the __call__(self, environ, start_response) method, which environ and start_response are passed in when make_pylons_stack are invoked during the configuration phase. If I want to override TrackingMiddleware Should I create another config/middleware/myTrackingMiddleware.py under ckanext-myext/ and then in plugin.py I implement the following?:
from myTrackingMiddleware import MyTrackingMiddleware
class MyPlugin(plugins.SingletonPlugin):
plugins.implements(plugins.IMiddleware, inherit=True)
def make_middleware(self, app, config):
tracking = MytrackingMiddleware(app, config)
return app
Update:
I tried to make the myTrackingMiddleware in an hierarchy and imported it in plugin.py, but I didn't received any request to '/_tracking' in myTrackingMiddleware.
I have implemented a set of process, and it works for myself. Basically, I kept what I have done as what I have mentioned in my own question. Then, if your middleware has some conflict with CKAN default Middleware, you probably have to completely disable the default one. I discussed with some major contributors of CKAN here: https://github.com/ckan/ckan/issues/4451. After I disabled CKAN ckan.tracking_enabled in dev.ini, I have the flexibility to get values from environ and handle tracking with my customized logic.
I was planning for my structure to be something similar to this:
appname/libs/user
/football
/tax
Where the library user, will have the models for the User, the controller which would show up the REST JSON API and the views.
The problems that I'm facing currently can be divided into two major questions, and which mainly stem from using Django for a while. I'm fairly new to CherryPy and SqlAlchemy.
How to define modles in each of this library? The problem I face is I've to inherit the Base Declarative and Engine in every model and run it as a standalone app for its models to be generated. Is there a mechanism where I can plug in the libraries and the database should pull all the models and create it? (Something that Django does.)
How to define routes/apis? (a urls.py)
How about defining the declarative base (sqlalchemy) in appname/db/__init__.py and for each of the libs import the base from appname in appname/libs/NAME/models.py:
import appname.db
Base = appname.db.Base
class MyUser(Base):
...
To get a database session just use a scoped session for example, this could be the basic content for appname/db/__init__.py (or just db.py if you don't want to define additional base models in appname/db/models.py)
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import engine_from_config
__all__ = ['session', 'ses']
ses = session = scoped_session(sessionmaker())
def load_engine(config):
engine = engine_from_config(config, prefix='')
session.configure(bind=engine)
Then set a tool to remove the session from the thread locals when the request has ended:
import cherrypy
from appname import db
def remove_db_session():
db.session.remove()
cherrypy.tools.removedbs = cherrypy.Tool('on_end_request', remove_db_session)
from that point forward just use the session as normal from any part of your application, for example:
from appname import db
from appname.libs.user import models
class User:
exposed = True
def GET(self, id):
db.ses.query(models.User).filter_by(id=id)
# and let the tool to remove the session
# when the request finish
By the way to enable that removedbs tool, just make sure that you execute somewhere that cherrypy.tools.removedbs = .... I usually put that in: appname/cptools/tool and then in the config dictionary or file set tools.removedbs.on to True
Working with cherrypy means that you will build the application tree, there is no extra magic you need to have a central place to build the full tree, if you want to use the MethodDispatcher or the DefaultDispatcher.
In this case I recommend you the MethodDispatcher, and probably this post can give you a little more perspective and this is from my blog emulating the github api without any base handler.
There is an alternative to use more django like routes with the RoutesDispatcher, but you will lose a lot of functionality from the tools.
To show you an example with the MethodDispatcher and building your own object tree, from the current structure you can have a build function on the appname/builder.py and make something like this:
from appname.views import Main
from appname.libs import user, football
appmap = {'user': user,
'footbal': football}
def build_app(*apps):
root = Main()
for app in apps:
appmodule = appmap[app]
appmodule.attach(root)
return root
And inside the appname/libs/user/__init__.py
from appname.libs.user import views
def build_tree():
root = views.Main()
root.management = views.Management()
return root
def attach(root):
root.user = build_tree()
That's just a way to structure the application, there is also a repository with cherrypy recipes which are pretty helpful.
I have a jinja_filters.py file with a few dozen custom filters I've written. Now I have multiple Flask apps that need to use these filters. (I'm not sure if my problem is Flask-specific or not.)
One hacky way to accomplish what I want is to do:
app = Flask(__name__)
import jinja_filters
#app.template_filter('filter_name1')
def filter_name1(arg):
return jinja_filters.filter_name1(arg)
#app.template_filter('filter_name2')
def filter_name2(arg):
return jinja_filters.filter_name2(arg)
...
What's the "right" way to do this?
EDIT: Ideally, I wouldn't have to list each filter name. So when I add a new filter to jinja_filters.py I don't have to update any other code -- all my apps would be able to use it right away.
There is a recommended way of doing this using Flask blueprints. One of it's use cases is this functionality specifically:
Provide template filters, static files, templates, and other utilities through blueprints. A blueprint does not have to implement applications or view functions.
You just need to create a flask.Blueprint object and use it to register your filters in a similar way as you would with flask.Flask app object, using the Blueprint.app_template_filter decorator or Blueprint.add_app_template_filter method.
# filters.py
import jinja2
import flask
blueprint = flask.Blueprint('filters', __name__)
# using the decorator
#jinja2.contextfilter
#blueprint.app_template_filter()
def filter1(context, value):
return 1
# using the method
#jinja2.contextfilter
def filter2(context, value):
return 2
blueprint.add_app_template_filter(filter2)
Then you just need to register the blueprint on your app object:
# app.py
import flask
import filters
app = flask.Flask(__name__)
app.register_blueprint(filters.blueprint)
And voilà, the filters are registered.
Where ever you're setting up your app object (app.py, perhaps), you only need to import your custom filters and then modify the Jinja environment attribute.
import jinja_filters
app = Flask(__name__)
app.jinja_env.filters['filter_name1'] = jinja_filters.filter_name1
app.jinja_env.filters['filter_name2'] = jinja_filters.filter_name2
and so on.
Another possibility is to use the inspect module to find all the methods in jinja_filters like so:
from inspect import getmembers, isfunction
import jinja_filters
app = Flask(__name__)
my_filters = {name: function
for name, function in getmembers(jinja_filters)
if isfunction(function)}
app.jinja_env.filters.update(my_filters)
That code is untested, but the idea is to build a dictionary of function names and functions that exist in your jinja_filters files and then update the Jinja environment's filters dictionary with your filters.