I am following the flaskr tutorial. When I run python flaskr.py, I get this error:
Traceback (most recent call last):
File "flaskr.py", line 26, in <module>
#app.before_request()
File "/Users/myname/anaconda/lib/python2.7/site-packages/flask/app.py", line 62, in wrapper_func
return f(self, *args, **kwargs)
TypeError: before_request() takes exactly 2 arguments (1 given)
However, on step 4 it specifically says that before_request() takes no arguments. I have carefully followed all the instructions. Why am I getting this error?
import sqlite3
from flask import Flask, g
DATABASE = '/tmp/flaskr.db'
app = Flask(__name__)
app.config.from_object(__name__)
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
#app.before_request()
def before_request():
g.db = connect_db()
#app.teardown_request()
def teardown_request(exception):
db = getattr(g, 'db', None)
if db is not None:
db.close()
if __name__ == '__main__':
app.run()
before_request is a decorator. Rather than calling it, you apply it directly to the decorated function.
#app.before_request
def my_before_request_function():
pass
teardown_request behaves the same way, so you need to change that too or you'll get the same error.
If you go back to the tutorial and look carefully at the code, you will notice that they do not call the decorators directly.
Decorators are called with the decorated function as the first (and only) argument. Another pattern of decorators is the "decorator factory", where a function does take arguments, producing the actual decorator (which just takes the implicit decorated function argument). Since before_request is not a factory, the docs just say it takes no arguments.
Related
I'm using python-dependency-injector.
I tried this code and it worked perfectly:
https://python-dependency-injector.ets-labs.org/providers/callable.html
that page also mentioned next:
Callable provider handles an injection of the dependencies the same way like a Factory provider.
So I went and wrote this code:
import passlib.hash
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
#inject
def bar(password_verifier=Provide[Container.password_verifier]):
pass
if __name__ == "__main__":
container = Container()
container.wire(modules=[__name__])
bar()
And it -- as you might expect -- didn't work. I received this error:
Traceback (most recent call last):
File "/home/common/learning_2022/code/python/blog_engine/test.py", line 20, in <module>
bar()
File "src/dependency_injector/_cwiring.pyx", line 26, in dependency_injector._cwiring._get_sync_patched._patched
File "src/dependency_injector/providers.pyx", line 225, in dependency_injector.providers.Provider.__call__
File "src/dependency_injector/providers.pyx", line 1339, in dependency_injector.providers.Callable._provide
File "src/dependency_injector/providers.pxd", line 635, in dependency_injector.providers.__callable_call
File "src/dependency_injector/providers.pxd", line 608, in dependency_injector.providers.__call
TypeError: GenericHandler.verify() missing 2 required positional arguments: 'secret' and 'hash'
I ran into this problem too and after much search I found the answer.
You can inject the callable by using the provider attribute:
import passlib.hash
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
# Use the 'provider' attribute on the provider in the container
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify).provider
#inject
def bar(password_verifier=Provide[Container.password_verifier]):
password_verifier(...)
if __name__ == "__main__":
container = Container()
container.wire(modules=[__name__])
bar()
The method passlib.hash.sha256_crypt.verify requires two positional arguments, secret and hash as shown here: https://passlib.readthedocs.io/en/stable/lib/passlib.hash.sha256_crypt.html
Because you're injecting an attribute of the Container, the DI framework must create an instance of this to inject it into the bar() method. The DI framework is unable to instantiate this object however since the required positional arguments are missing.
If you wanted to use the method in bar(), you'd have to do so without provoking the DI framework to try to create an instance of the object at initialisation time. You could do this with the following:
import passlib.hash
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
password_hasher = providers.Callable(
passlib.hash.sha256_crypt.hash,
salt_size=16,
rounds=10000,
)
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
#inject
def bar(container=Provide[Container]):
hashed_password = container.password_hasher("123")
assert container.password_verifier("123", hashed_password)
if __name__ == "__main__":
container = Container()
container.wire(modules=[__name__])
bar()
I would like to use flask to run some functions. Assume you have a file called myapp.py with a function run
def run():
return 'special routed hello world'
and this main flask file, something like this
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'hello world'
#app.route('/<myapp>')
def open_app(myapp):
from myapp import run
return run()
So obvisouly that doesn't work, but how can I dynamically call these run functions when flask is already running. Is this even possible?
In other words: when someone opens for example .../foobar, the function open_app with parameter foobar is begin called. In that function, from the file foobar.py (let's assume that file exists) import function run, run it and return the result from that function.
In fact, it is possible to use importlib, especially import_modulein combination with getattr, to dynamically call up functions of a module. And I have security concerns too.
The following two examples show a kind of simple RPC implementation.
The first example uses a dictionary for modules. If a module with the name is available, the run function is called. It allows a strong restriction. Optimization is certainly possible and probably necessary.
The second example shows a possibility of calling different functions within different modules with parameters. In spite of everything, as with the previous version, all modules are in one package called "actions" to ensure that calls can be limited. I also think a variant with POST is more suitable for this purpose than using variable rules.
Remember these are strong simplifications. Protocols such as JSON-RPC will certainly help as a guide during implementation.
from flask import Flask
from flask import jsonify, request, jsonify
from importlib import import_module
from actions import *
app = Flask(__name__)
app.secret_key = 'your secret here'
#app.route('/exec/<string:action>', methods=['POST'])
def exec(action):
result = cmddict[action].run()
return jsonify(result=result)
#app.route('/call', methods=['POST'])
def call():
data = request.get_json()
module = data.get('module')
method = data.get('method')
params = data.get('params')
try:
# import module by name
m = import_module(f'actions.{module}', __name__)
# get function by name
f = getattr(m, method)
# call function with params
result = f(**params) if isinstance(params, dict) else f(*params)
return jsonify(result=result, error=None)
except Exception as err:
return jsonify(result=None, error=f'{err}')
# ./actions/__init__py
__all__ = ['demo']
from importlib import import_module
cmddict = {}
for _ in __all__:
cmddict[_] = import_module(f'actions.{_}', __name__)
__all__.append('cmddict')
# ./actions/demo.py
def run():
return f'hello world'
def func(*args, **kwargs):
print('func', args, kwargs)
``
I am trying to understand the examples for replacing the use of try-finally and flag variables in Python's documentation
According to the documentation instead of:
cleanup_needed = True
try:
result = perform_operation()
if result:
cleanup_needed = False
finally:
if cleanup_needed:
cleanup_resources()
we could use a small ExitStack-based helper class Callback like this (I added the perform_operation and cleanup_resources function):
from contextlib import ExitStack
class Callback(ExitStack):
def __init__(self, callback, /, *args, **kwds):
super(Callback, self).__init__()
self.callback(callback, *args, **kwds)
def cancel(self):
self.pop_all()
def perform_operation():
return False
def cleanup_resources():
print("Cleaning up resources")
with Callback(cleanup_resources) as cb:
result = perform_operation()
if result:
cb.cancel()
I think the code simulates the exceptional case, where the perform_operation() did not run smoothly and a cleanup is needed (perform_operation() returned False). The Callback class magically takes care of the running the cleanup_resources() function (I can't quite understand why, by the way).
Then I simulated the normal case, where everything runs smoothly and no cleanup is needed, I changed the code to make perform_operation() return True instead. In this case, however, the cleanup_resources function also runs and the code errors out:
$ python minimal.py
Cleaning up resources
Traceback (most recent call last):
File "minimal.py", line 26, in <module>
cb.cancel()
File "minimal.py", line 12, in cancel
self.pop_all()
File "C:\ProgramData\Anaconda3\envs\claw\lib\contextlib.py", line 390, in pop_all
new_stack = type(self)()
TypeError: __init__() missing 1 required positional argument: 'callback'
Can you explain what exactly is going on here and how this whole ExitStack and callack stuff works?
I'm trying to create a role wrapper which will allow me to restrict certain pages and content for different users. I already have methods implemented for checking this, but the wrapper/decorator for implementing this fails and sometimes doesn't, and I have no idea of what the cause could be.
I've searched around looking for a conclusive reason as to what is causing this problem, but unfortunately, Flask's tracebacks do not give a conclusive reason or solution, like most other searches I come up with do.
I'm using Flask-Login, Flask-Migrate, and Flask-SQLAlchemy to manage my web application, I've looked into different methods of applying RBAC, but they all required seemingly complicated changes to my database models, and I felt that my method would have a higher chance of working in the long run.
Here is my simplified code (I can provide the full application if requested). Below that is the full traceback from the debugger.
Thank you.
routes.py
def require_role(roles=["User"]):
def wrap(func):
def run(*args, **kwargs):
if current_user.is_authenticated:
if current_user.has_roles(roles):
return func(*args, **kwargs)
return abort(401)
return run
return wrap
#app.route('/hidden<id>/history')
#login_required
#require_role(roles=['Admin'])
def hidden_history(id):
if not validate_id(id):
return '<span style="color: red;">error:</span> bad id'
return render_template('hidden_history.html')
#app.route('/hidden<id>/help')
#login_required
def hidden_help(id):
if not validate_id(id):
return '<span style="color: red;">error:</span> bad id'
return render_template('hidden_help.html')
#app.route('/hidden<id>/')
#login_required
#require_role(roles=['Hidden'])
def hidden(id):
if not validate_id(id):
return '<span style="color: red;">error:</span> bad id'
# ...
return render_template('hidden.html')
Traceback (most recent call last)
Traceback (most recent call last):
File "A:\Programming\Python\Flask\xevion.dev\wsgi.py", line 1, in <module>
from app import app, db
File "A:\Programming\Python\Flask\xevion.dev\app\__init__.py", line 18, in <module>
from app import routes, models
File "A:\Programming\Python\Flask\xevion.dev\app\routes.py", line 143, in <module>
#require_role(roles=['Hidden'])
File "c:\users\xevion\appdata\local\programs\python\python36\lib\site-packages\flask\app.py", line 1251, in decorator
self.add_url_rule(rule, endpoint, f, **options)
File "c:\users\xevion\appdata\local\programs\python\python36\lib\site-packages\flask\app.py", line 67, in wrapper_func
return f(self, *args, **kwargs)
File "c:\users\xevion\appdata\local\programs\python\python36\lib\site-packages\flask\app.py", line 1222, in add_url_rule
'existing endpoint function: %s' % endpoint)
AssertionError: View function mapping is overwriting an existing endpoint function: run
Edit: I realize now that it doesn't work when there are more than one calls to the wrapper function. How come?
So to solve the problem that has been plaguing me for the last couple of hours, I've looked into how the flask_login module actually works, and after a bit of investigating, I found out that they use an import from functools called wraps.
I imported that, copied how flask_login implemented it essentially, and my app is now working.
def require_role(roles=["User"]):
def wrap(func):
#wraps(func)
def decorated_view(*args, **kwargs):
if current_user.is_authenticated:
if current_user.has_roles(roles):
return func(*args, **kwargs)
return abort(401)
return decorated_view
return wrap
flask_login/utils.py#L264-L273
At first glance it looks like a conflict with your run function in the require_role decorator (docs):
def require_role(roles=["User"]):
def wrap(func):
def wrapped_func(*args, **kwargs):
...
I'm currently running a Python Flask application that makes use of the following decorator:
def login_required(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
if 'phone' in session:
user = User.query.filter_by(phone = session['phone']).first()
if user:
return fn(user, *args, **kwargs)
else:
return redirect(url_for('login'))
return wrapper
I have the following view:
#app.route('/delete/<eventid>')
#login_required
def delete(eventid):
That is being called by the following line:
url_for('delete', eventid=event.uid)
This produces the following error:
delete() got multiple values for keyword argument 'eventid'
The delete() function works when the decorator is NOT used.
The decorator works when it is NOT used with the delete() function.
How do I go about solving this problem?
Your wrapper function passes an additional argument user to the decorated function (in this case delete)
return fn(user, *args, **kwargs)
But your delete function only takes a single arg called eventid. Python interprets the first arg (user) as the eventid arg, but then gets another keyword argument for the same name, hence the odd error message.
It's essentially like this:
>>> def delete(eventid):
print eventid
>>> delete('user', eventid='test')
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
delete('user', eventid='test')
TypeError: delete() got multiple values for keyword argument 'eventid'
So it works without the decorator because no user arg is passed in. Just add the user as the first arg to the delete function.