Python Decorators and Classes - python

I am working on a web server using Flask and Flask-Login. It was working until I needed to move it to a class. It is broken because of decorators: before I had the code
login_manager = LoginManager(app)
...
#login_required
def control_panel():
return render_template(...)
But this now becomes:
class webserver:
def __init__(self):
self.login_manager = LoginManager(app)
...
#login_required
def control_panel(self):
return self.render_template(...)
The issue is that I really need to write,
#self.login_required
But it is not valid syntax. How can I overcome this?
Thanks!

I'm not sure how your LoginManager creates the decorator, but you could make it work like this:
class LoginManager:
def __init__(self, app):
pass
#staticmethod
def login_required(func):
def inner(*args, **kwargs):
print "login_required: before"
func(*args, **kwargs)
print "login_required: after"
return inner
class Webserver:
def __init__(self):
self.login_manager = LoginManager(app)
#LoginManager.login_required
def control_panel(self):
print "control_panel: called"
app = {}
ws = Webserver()
ws.control_panel()
Output:
login_required: before
control_panel: called
login_required: after

Related

How to fix decorator mock

I have a problem with mocking my decorator in my flask app.
I've tried monkeypatch, mock.patch and none is working
I'm running with python3.7
Here is my code
in => app/v1/views/user_data.py
#r.route('/myroute/<int:user_id>', methods=['GET'])
#verify_token
def ep_intern_midas_user_data_user_id(user_id):
# do some stuff here
return "Done"
in app/v1/decorators/verify_token.py
def verify_token(view_function):
#wraps(view_function)
def decorated_function(*args, **kwargs):
# do decorator stuff
return decorated_function
in tests/unit_tests/views/user_data.py
from functools import wraps
from app.v1 import decorators
def mock_decorator():
def decorator(f):
#wraps(f)
def decorated_function(*args, **kwargs):
return 200
return decorated_function
return decorator
# !important thing - import of app after replace
decorators.verify_token = mock_decorator
from app import app
app.testing = True
def test_index():
with app.test_client() as client:
res = client.get('v1/intern/midas/user_data/1')
assert res.status_code == 200
(even with monkeypatch)
===== never works
I expected to have the result 200 but I have 401, what is the result if the decorator is called and not its mock.
Thanks for help

Access view configuration from renderer

Is there a way to access the view configuration from a renderer? By view configuration I mean the arguments passed to the view_config() decorator. My goal is to add some settings to the view configuration which a renderer can then use.
I have a custom renderer:
class MyRenderer(object):
def __init__(self, info):
pass
def __call__(self, value, system):
# Get view options.
my_renderer_opts = ...
# Render using options.
...
which is registered as:
config.add_renderer('my_renderer', MyRenderer)
Then in my view I have:
class Page(object):
def __init__(self, request):
self.request = request
#pyramid.view.view_config(
route_name='root',
renderer='my_renderer',
my_renderer_opts={...}
)
def view(self):
pass
Is there a way to access my_renderer_opts passed to view_config() from MyRenderer.__call__()?
if you still want implement it as described, maybe deriver will be helpfull:
from wsgiref.simple_server import make_server
from pyramid.view import view_config
from pyramid.config import Configurator
#view_config(route_name="hello", renderer="myrend", renderer_options={"a": 1})
def hello_world(request):
return "Hello World!"
def rendereropt_deriver(view, info):
options = info.options.get("renderer_options", {})
def wrapped(context, request):
setattr(request, "_renderer_options", options)
return view(context, request)
return wrapped
rendereropt_deriver.options = ("renderer_options",)
class MyRendererFactory:
def __init__(self, info):
self.info = info
def __call__(self, value, system):
options = getattr(system["request"], "_renderer_options", {})
print("`renderer_options` is {}".format(options))
return value
if __name__ == "__main__":
with Configurator() as config:
config.add_route("hello", "/")
config.add_view_deriver(rendereropt_deriver)
config.add_renderer("myrend", MyRendererFactory)
config.scan(".")
app = config.make_wsgi_app()
server = make_server("0.0.0.0", 8000, app)
server.serve_forever()

Flask routing to view functions via inheritance

I am currently in the process of writing a Flask application that routes endpoints to a variety of "Actions." These actions all implement a parent function called "run()"
In code:
import abc
class Action(object):
__metaclass__ = abc.ABCMeta
#classmethod
def authenticated(self):
print("bypassing action authentication")
return True
#classmethod
def authorized(self):
print("bypassing action authorization")
return True
#classmethod
#abc.abstractmethod
def execute(self):
raise NotImplementedError("must override execute!")
#classmethod
def response(self, executeResult):
return executeResult
#classmethod
def run(self):
result = ""
if self.authenticated() & self.authorized():
result = self.execute()
return self.response(result)
The intent is that all actually used actions are derived members of this Action class that bare-minimum implement an execute() function that differentiates them. Unfortunately, when I attempt to add routes for these
app.add_url_rule('/endone/', methods=['GET'], view_func=CoreActions.ActionOne.run)
app.add_url_rule('/endtwo/', methods=['GET'], view_func=CoreActions.ActionTwo.run)
I receive the following error:
AssertionError: View function mapping is overwriting an existing endpoint function: run
Does anyone know a possible solution to this issue? Thanks!
The common approach of generating view functions is to use Flask views. Subclass your Action class from flask.views.View, dispatch_request method is used instead of run:
import abc
from flask.views import View
class Action(View):
__metaclass__ = abc.ABCMeta
def authenticated(self):
print("bypassing action authentication")
return True
def authorized(self):
print("bypassing action authorization")
return True
#abc.abstractmethod
def execute(self):
raise NotImplementedError("must override execute!")
def response(self, executeResult):
return executeResult
def dispatch_request(self):
result = ""
if self.authenticated() & self.authorized():
result = self.execute()
return self.response(result)
And you can add routes using View.as_view() method which convert your class to view function:
app.add_url_rule(
'/endone/',
methods=['GET'],
view_func=CoreActions.ActionOne.as_view('endone')
)

Flask blueprint unit-testing

Is there a good practice to unit-test a flask blueprint?
http://flask.pocoo.org/docs/testing/
I didn't found something that helped me or that is simple enough.
// Edit
Here are my code:
# -*- coding: utf-8 -*-
import sys
import os
import unittest
import flask
sys.path = [os.path.abspath('')] + sys.path
from app import create_app
from views import bp
class SimplepagesTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app('development.py')
self.test_client = self.app.test_client()
def tearDown(self):
pass
def test_show(self):
page = self.test_client.get('/')
assert '404 Not Found' not in page.data
if __name__ == '__main__':
unittest.main()
In this case, i test the blueprint. Not the entire app. To test the blueprint i've added the root path of the app to sys.path. Now i can import the create_app function to ...create the app. I also init the test_client.
I think i've found a good solution. Or will is there a better way?
I did the following if this helps anyone. I basically made the test file my Flask application
from flask import Flask
import unittest
app = Flask(__name__)
from blueprint_file import blueprint
app.register_blueprint(blueprint, url_prefix='')
class BluePrintTestCase(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
def test_health(self):
rv = self.app.get('/blueprint_path')
print rv.data
if __name__ == '__main__':
unittest.main()
Blueprints are very similar to application. I guess that you want test test_client requests.
If you want test blueprint as part of your application then look like no differences there are with application.
If you want test blueprint as extension then you can create test application with own blueprint and test it.
I have multiple APIs in one app and therefore multiple blueprints with url_prefix. I did not like that I have to prefix all paths when testing on of the APIs. I used the following class to wrap the test_client for blueprints:
class BlueprintClient():
def __init__(self, app_client, blueprint_url_prefix):
self.app_client = app_client
self.blueprint_url_prefix = blueprint_url_prefix.strip('/')
def _delegate(self, method, path, *args, **kwargs):
app_client_function = getattr(self.app_client, method)
prefixed_path = '/%s/%s' % (self.blueprint_url_prefix, path.lstrip('/'))
return app_client_function(prefixed_path, *args, **kwargs)
def get(self, *args, **kwargs):
return self._delegate('get', *args, **kwargs)
def post(self, *args, **kwargs):
return self._delegate('post', *args, **kwargs)
def put(self, *args, **kwargs):
return self._delegate('put', *args, **kwargs)
def delete(self, *args, **kwargs):
return self._delegate('delete', *args, **kwargs)
app_client = app.test_client()
api_client = BlueprintClient(app_client, '/api/v1')
api2_client = BlueprintClient(app_client, '/api/v2')

Can I have all of my unit tests run in the Flask app context?

For every test I write that uses my apps models, I seem to have to use the current apps context:
SomeTestCase(unittest2.TestCase):
setUp(self):
self.app = Flask(__name__)
...
test_something(self):
with self.app.app_context():
# Do something
Is there a way to tell all of my tests to run using the current apps context to save me having this line in all of my tests?
I found the answer I was looking for by looking at the way the Flask-Testing extensions TestCase sets itself up, i.e., pushing the testing context to the _ctx stack inside a function that's called from within it's __call__ method.
class BaseTestCase(unittest2.TestCase):
def __call__(self, result=None):
try:
self._pre_setup()
super(BaseTestCase, self).__call__(result)
finally:
self._post_teardown()
def _pre_setup(self):
self.app = create_app()
self.client = self.app.test_client()
self._ctx = self.app.test_request_context()
self._ctx.push()
def _post_teardown(self):
if getattr(self, '_ctx') and self._ctx is not None:
self._ctx.pop()
del self._ctx
And my test:
class SomeTestCase(BaseTestCase):
test_something(self):
# Test something - we're using the right app context here
You can try something like below.
DISCLAIMER: I just came up with the idea and didn't test this solution thoroughly although it seems to work. It is also IMHO rather ugly.
from functools import wraps
def with_context(test):
#wraps(test)
def _with_context(self):
with self.app.app_context():
test(self)
return _with_context
SomeTestCase(unittest2.TestCase):
setUp(self):
self.app = Flask(__name__)
...
#with_context
test_something(self):
# Do something
Depending on how you test you may be able to use the test client. Example:
SomeTestCase(unittest2.TestCase):
setUp(self):
self.app = Flask(__name__)
self.client = self.app.text_client()
test_something(self):
response = self.client.get('/something')
# check response

Categories

Resources