I am trying to follow the instructions in http://flask.pocoo.org/docs/0.12/patterns/celery/ so I can perform flask/socketIO operations in celery tasks. My directory structure is a bit different however and I'm not having any luck with imports.
My directory structure is as follows:
├── app
│ ├── __init__.py
│ ├── __pycache__
│ ├── auth.py
│ ├── ctasks.py
│ ├── helper.py
│ ├── saml.py
│ ├── socks.py
│ ├── templates
│ ├── threads.py
│ └── views.py
├── app.py
├── config.py
├── requirements.txt
└── saml
├── dev
└── prod
I call the app from app.py
from app import socketio, app
if __name__ == '__main__':
socketio.run(app, debug=True, port=443, ssl_context='adhoc')
__init__.py
from flask import Flask, request
from flask_socketio import SocketIO
from .ctasks import subtaskcaller, make_celery
from .helper import wait_to_finish
async_mode = None
app = Flask(__name__)
app.config.from_object('config')
socketio = SocketIO(app, async_mode=async_mode)
cel = make_celery(app)
from .auth import SamlManager
saml_manager = SamlManager()
saml_manager.init_app(app)
from app import views, socks, saml, helper, ctasks
ctasks.py
from celery import Celery
from config import *
from .helper import wait_to_finish, emitter
import time
from app import cel
def make_celery(app):
c = Celery(app.import_name, backend=CELERY_RESULT_BACKEND, broker=CELERY_BROKER_URL)
c.conf.update(app.config)
taskbase = c.Task
class ContextTask(taskbase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return taskbase.__call__(self, *args, **kwargs)
c.Task = ContextTask
return c
#cel.task(name='tasks.tester', serializer='pickle')
def tester():
emitter('emit from subsubtask')
for i in range(1, 50):
time.sleep(1)
print('test {0}'.format(i))
x = True
return x
#cel.task(name='task.subtaskcaller', serializer='pickle')
def subtaskcaller():
emitter('emit from subtask')
finished = tester.delay()
wait_to_finish(finished)
return finished
I am getting an error when trying to import cel from app in ctasks.py:
ImportError: cannot import name 'cel'
In your __init__.py you only have cel. You don't have an object called celery in your __init__ file, so it can't be imported to some other file. You can try from app import cel.˛
EDIT:
in __init__ you from .ctasks import subtaskcaller, make_celery
but in ctasks you import cel from app (which doesn't exist yet at that point, only Flask, request, and SocketIO exist at that point in time).
So you need to put your #cel decorated functions in yet another script, which you can import all the way at the bottom of __init__
Related
I'm writing unit tests for a codebase. My issue is I need to patch a database abstraction class but it is masked by the __init__.py file. My file structure is:
.
├── __init__.py
├── tools
│ ├── __init__.py
│ ├── test
│ │ ├── __init__.py
│ │ └── test_tool1.py
│ ├── tool1.py
│ └── tool2.py
└── utils
├── __init__.py
└── sql_client.py
Now the content of the files:
# tools/__init__.py
from .tool1 import * # `SQLClient` is imported here as part of tool1
from .tool2 import * # `SQLClient` is imported here again but now it's part of tool2
# tools/tool1.py
from utils import SQLClient
class A(object):
...
def run(self, **kwargs):
# the function I want to test
sql = SQLClient("some id")
# tools/tool2.py
from utils import SQLClient
...
# utils/__init__.py
from sql_client import *
# utils/sql_client.py
class SQLClient(object):
# The sql abstraction class that needs to be patched
In my test file, I'm creating a mock class to be used as the patch. I'm using absolute imports in my tests because later I want to move all the tests outside of the source folders.
# tools/test/test_tool1.py
from unittest.mock import MagicMock
from utils import SQLClient
from tools import A
class MockSQLClient(MagicMock):
def __init__(self, *args, **kwargs):
super().__init__(spec=SQLClient)
self._mocks = {"select *": "rows"}
def make_query(query)
return self._mocks[query]
def test_run_func(monkeypatch):
monkeypatch.setattr("tools.SQLClient", MockSQLClient)
a = A()
a.run()
# rest of the test
Now, the issue is that tools/__init__.py imports everything from all the submodules so SQLClient from tool1 is being masked by SQLClient from tool2. As a result, my monkeypatch is patching tool2.SQLClient. I cannot patch the tool1 part directly by monkeypatch.setattr("tools.tool1.SQLClient") because of tools/__init__.py which throws an error complaining there is no tool1 in tools.
EDIT
changed question title, more detail on where the test is.
I am working on building some basic functions. One of them what I am doing now is timeout-session. I set PERMANENT_SESSION_LIFETIME = timedelta(minutes=20) and I found it works well.
But what I am confused is How to tell users that the user's session was expired because of session lifetime using flash?
Or is there a way to redirect when the user's session was expired?
Below is Specifics
tree
.
├── __init__.py
├── admin
│ ├── __init__.py
│ ├── forms.py
│ └── views.py
├── app.py
├── commands.py
├── compat.py
├── database.py
├── dataset
│ ├── __init__.py
│ ├── forms.py
│ ├── models.py
│ └── views.py
├── decorators.py
├── extensions.py
├── public
│ ├── __init__.py
│ ├── __pycache__
│ ├── forms.py
│ └── views.py
├── settings.py
settings.py
from datetime import timedelta
from environs import Env
env = Env()
env.read_env()
ENV = env.str("FLASK_ENV", default="production")
DEBUG = ENV == "development"
SQLALCHEMY_DATABASE_URI = env.str("DATABASE_URL")
SECRET_KEY = env.str("SECRET_KEY")
SEND_FILE_MAX_AGE_DEFAULT = env.int("SEND_FILE_MAX_AGE_DEFAULT")
BCRYPT_LOG_ROUNDS = env.int("BCRYPT_LOG_ROUNDS", default=13)
DEBUG_TB_ENABLED = DEBUG
DEBUG_TB_INTERCEPT_REDIRECTS = False
CACHE_TYPE = "simple" # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
MONGODB_URI = env.str("MONGODB_URI")
MONGODB_DATABASE_NAME = env.str("MONGODB_DATABASE_NAME")
UPLOAD_FOLDER = env.str("UPLOAD_FOLDER")
PERMANENT_SESSION_LIFETIME = timedelta(minutes=100)
app.py
# -*- coding: utf-8 -*-
"""The app module, containing the app factory function."""
import logging
import sys
from flask import Flask, render_template
from web import admin, commands, public, user, dataset
from web.extensions import (
bcrypt,
cache,
csrf_protect,
db,
debug_toolbar,
flask_static_digest,
login_manager,
migrate,
)
def create_app(config_object="web.settings"):
"""Create application factory
:param config_object: The configuration object to use.
"""
app = Flask(__name__.split(".")[0])
app.config.from_object(config_object)
register_extensions(app)
register_blueprints(app)
register_errorhandlers(app)
register_shellcontext(app)
register_commands(app)
configure_logger(app)
return app
``
I quoted this code from here.
login_mgr = LoginManager(app)
login_mgr.login_view = 'login'
login_mgr.refresh_view = 'relogin'
login_mgr.needs_refresh_message = (u"Session timedout, please re-login")
login_mgr.needs_refresh_message_category = "info"
Use a decorator before_request, it runs before each request.
https://flask.palletsprojects.com/en/2.0.x/api/#flask.Flask.before_request
#app.before_request
def load_user():
if "user_id" in session:
g.user = db.session.get(session["user_id"])
In your case, refer this answer.
#app.before_request
def before_request()
now = datetime.datetime.now()
try:
last_active = session['last_active']
delta = now - last_active
if delta.seconds > 1800:
session['last_active'] = now
return logout('Your session has expired after 30 minutes, you have been logged out')
except:
pass
try:
session['last_active'] = now
except:
pass
https://stackoverflow.com/a/48768278/1474183
Not sure if this is still something you are looking for, but I also had the same problem. I had implemented what Rafael Ribeiro was referring to above but needed a way to show the user before they started interacting with the page. My solution. I used the login_mgr settings:
login_mgr = LoginManager(app)
login_mgr.login_view = 'login'
login_mgr.refresh_view = 'relogin'
login_mgr.needs_refresh_message = (u"Session timedout, please re-login")
login_mgr.needs_refresh_message_category = "info
Then in the template, I have some JavaScript that makes a fetch to a route that requires a login to view. And on error, I refresh my page, which triggers the redirect.
Setting the expiry:
#main.before_request
def before_request():
session.permanent = True
current_app.permanent_session_lifetime = timedelta(hours=24)
Any route that has #login_required
#main.route("/check-session", methods=["Get", "Post"])
#login_required
def check_session():
return {"session": "checked"}
Javascript:
<script>
function fetchStatus() {
fetch(`/check-session`, {
method: "GET"
})
.then(response => response.json())
.catch(err => {
window.location.reload();
})
}
window.addEventListener('load', function () {
// Your document is loaded.
var fetchInterval = 86700000; // 24 hours 5 mins.
setInterval(fetchStatus, fetchInterval);
});
</script>
This seems to do the trick
When I run this on my terminal command line:
gunicorn app:application --preload -b 0.0.0.0:5000
I am getting the following error:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/gunicorn/util.py", line 401, in import_app
app = getattr(mod, name)
AttributeError: module 'app' has no attribute 'application'
And when I go to the errror file, I have:
def import_app(module):
parts = module.split(":", 1)
if len(parts) == 1:
obj = "application"
else:
module, obj = parts[0], parts[1]
try:
mod = importlib.import_module(module)
except ImportError:
if module.endswith(".py") and os.path.exists(module):
msg = "Failed to find application, did you mean '%s:%s'?"
raise ImportError(msg % (module.rsplit(".", 1)[0], obj))
raise
# Parse obj as a single expression to determine if it's a valid
# attribute name or function call.
try:
expression = ast.parse(obj, mode="eval").body
except SyntaxError:
raise AppImportError(
"Failed to parse %r as an attribute name or function call." % obj
)
if isinstance(expression, ast.Name):
name = expression.id
args = kwargs = None
elif isinstance(expression, ast.Call):
# Ensure the function name is an attribute name only.
if not isinstance(expression.func, ast.Name):
raise AppImportError("Function reference must be a simple name: %r" % obj)
name = expression.func.id
# Parse the positional and keyword arguments as literals.
try:
args = [ast.literal_eval(arg) for arg in expression.args]
kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expression.keywords}
except ValueError:
# literal_eval gives cryptic error messages, show a generic
# message with the full expression instead.
raise AppImportError(
"Failed to parse arguments as literal values: %r" % obj
)
else:
raise AppImportError(
"Failed to parse %r as an attribute name or function call." % obj
)
is_debug = logging.root.level == logging.DEBUG
try:
app = getattr(mod, name)
except AttributeError:
if is_debug:
traceback.print_exception(*sys.exc_info())
raise AppImportError("Failed to find attribute %r in %r." % (name, module))
# If the expression was a function call, call the retrieved object
# to get the real application.
if args is not None:
try:
app = app(*args, **kwargs)
except TypeError as e:
# If the TypeError was due to bad arguments to the factory
# function, show Python's nice error message without a
# traceback.
if _called_with_wrong_args(app):
raise AppImportError(
"".join(traceback.format_exception_only(TypeError, e)).strip()
)
# Otherwise it was raised from within the function, show the
# full traceback.
raise
if app is None:
raise AppImportError("Failed to find application object: %r" % obj)
if not callable(app):
raise AppImportError("Application object must be callable.")
return app
I don´t really know what´s going here and why I am getting this error. I passed from python 2 to python 3 and I am reinstalling all the modules and packages but this error suddenly appeared. Thanks in advance
This is my directory tree:
FontsFree-Net-SFProDisplay-Bold.ttf
├── Procfile
├── __pycache__
├── app.py
├── contactbook.db
├── db.sqlite3
├── flask
│ ├── bin
│ ├── lib
│ └── pyvenv.cfg
├── helpers.py
├── requirements.txt
├── runtime.txt
├── socialauthphp
│ ├── App
│ ├── assets
│ ├── composer.json
│ ├── composer.lock
│ ├── hydridauth.php
│ ├── index.php
│ ├── logout.php
│ └── vendor
├── static
│ ├── Contacto.vcf
│ ├── icons
│ ├── img
│ ├── main.css
│ └── qr
└── templates
├── contact.html
├── index.html
├── layout.html
├── login.html
├── profile.html
├── qrcode.html
├── register.html
└── view.html
part of app.py:
import os
import vobject
from werkzeug.datastructures import FileStorage
from werkzeug.utils import secure_filename
from flask_qrcode import QRcode
from django.core.exceptions import ValidationError
from django import forms
from flask_login import UserMixin
from flask import Flask
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired
from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class
from wtforms import SubmitField
from flask_login import LoginManager, login_required, login_user, current_user, login_manager
from flask import Flask, flash, jsonify, redirect, render_template, request, session, url_for
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash
from helpers import login_required
import sys
app = Flask(__name__)
login = LoginManager(app)
QRcode(app)
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Ensure responses aren't cached
#app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///contactbook.db'
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
db = SQLAlchemy(app)
``````
It is an error in your execution command gunicorn app:application --preload -b 0.0.0.0:5000
The gunicorn's docs (https://docs.gunicorn.org/en/stable/run.html) says that
gunicorn [OPTIONS] APP_MODULE
where APP_MODULE is $(MODULE_NAME):$(VARIABLE_NAME), your module name is app but your variable name is not application, it is also app.
so, the correct command is
gunicorn app:app --preload -b 0.0.0.0:5000
This is my first time creating a project using python and flask. I intend to use SQLAlchemy models along too. and this is a fairly bigger project. As of now, I have divided the project in 2 Blueprints : site and the api. After organizing the project, I am confused as to how can I connnect these models with the database and do I need to re-organize the structure as I am not fully aware of nature of flask.
so this is the directory structure of the dir app/ in my base repository:
`
.
├── Blueprints
│ ├── __init__.py
│ ├── __pycache__
│ │ └── __init__.cpython-36.pyc
│ ├── api
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-36.pyc
│ │ │ └── routes.cpython-36.pyc
│ │ └── routes.py
│ ├── config.py
│ └── site
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ └── routes.cpython-36.pyc
│ ├── operations.py
│ ├── routes.py
│ ├── static
│ └── templates
│ ├── about.html
│ ├── contact.html
│ ├── home.html
│ ├── login.html
│ ├── services.html
│ └── stories.html
├── __main__.py
├── __pycache__
│ └── __main__.cpython-36.pyc
└── models
├── Attendance.py
├── Batch.py
├── Course.py
├── Module.py
├── Student.py
├── Test.py
└── __init__.py
`
Please ignore Pycache, as this is auto generated.
now I cannot figure out a way as to how to import and use these models in api and site, neither I can understand as to how am I supposed to fetch the db object created in /Blueprints/__init__.py to all the models.
I understand that this question is not upto the standards of stack overflow questions, BUT I personally feel that organizing a flask project is itself very confusing with each tutorial or forum I see, having their own perspectives of organizing it.
There's several ways to organize a project, but the __init__.py file contained inside the app/ folder is what links a lot of it together. Here's the contents of one of my project's __init__.py file:
from werkzeug.contrib.fixers import ProxyFix
from flask import Flask, session
from app.config import (PERMANENT_SESSION_LIFETIME_MS, Time_Before_Warning,
Min_Ping_Interval)
import datetime
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
# Setup the app with the config.py file
app.config.from_pyfile('config.py')
# Setup the logger
from app.logger_setup import logger, log_view
# Setup the database
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
#setup zipcode database
from pyzipcode import ZipCodeDatabase
zdb = ZipCodeDatabase()
# Setup the mail server
from flask.ext.mail import Mail
mail = Mail(app)
# Setup the debug toolbar
#from flask_debugtoolbar import DebugToolbarExtension
#app.config['DEBUG_TB_TEMPLATE_EDITOR_ENABLED'] = False
#app.config['DEBUG_TB_PROFILER_ENABLED'] = False
#toolbar = DebugToolbarExtension(app)
# Setup the password crypting
from flask.ext.bcrypt import Bcrypt
bcrypt = Bcrypt(app)
# Import the views
from app.views import (main, user, error, request, upload, dashboard, org,
msgs, notifications, download, reports,
direct_send,provider,utils)
app.register_blueprint(user.userbp)
app.register_blueprint(request.requestbp)
app.register_blueprint(upload.uploadbp)
app.register_blueprint(dashboard.dashboardbp)
app.register_blueprint(org.orgbp)
app.register_blueprint(msgs.msgbp)
app.register_blueprint(notifications.notificationsbp)
app.register_blueprint(download.downloadbp)
app.register_blueprint(reports.reportsbp)
app.register_blueprint(direct_send.directsendbp)
app.register_blueprint(provider.providerbp)
app.register_blueprint(utils.utilsbp)
# Setup the user login process
from flask.ext.login import LoginManager, current_user
from app.models import User, View
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'userbp.signin'
#login_manager.user_loader
def load_user(email):
return User.query.filter(User.email == email).first()
from flask.ext.principal import identity_loaded, RoleNeed, UserNeed
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
# Set the identity user object
identity.user = current_user
# Add the UserNeed to the identity
if hasattr(current_user, 'id'):
identity.provides.add(UserNeed(current_user.id))
# Assuming the User model has a list of roles, update the
# identity with the roles that the user provides
if hasattr(current_user, 'roles'):
identity.provides.add(RoleNeed(current_user.roles.type))
from flask.ext.principal import Principal
# load the extension
principals = Principal(app)
# Create a permission with a single Need, in this case a RoleNeed.
#from app import admin
#app.before_request
def make_session_permanent():
session.permanent = True
lt = PERMANENT_SESSION_LIFETIME_MS / (60*1000)
app.permanent_session_lifetime = datetime.timedelta(minutes=lt)
#app.context_processor
def add_session_config():
"""
Add current_app.permanent_session_lifetime converted to milliseconds
to context.
"""
return {
'PERMANENT_SESSION_LIFETIME_MS': PERMANENT_SESSION_LIFETIME_MS,
'Time_Before_Warning': Time_Before_Warning,
'Min_Ping_Interval': Min_Ping_Interval,
}
And then inside one of the blueprints:
from flask import (Blueprint, render_template, redirect, url_for,
abort, flash, request)
from flask.ext.login import login_required, current_user
from app import app, models, db, log_view, config
from app.models import (Groups, Organizations, OrgHasOwner, UserHasGroups,
GroupHasOwner, User, Fax, FavoriteGroups)
from app.forms import org as org_forms
from app.toolbox import email, misc, s3, fax
from sqlalchemy.sql import func
from werkzeug import secure_filename
from uuid import uuid4
import datetime
import string
import os
# Create a user blueprint
orgbp = Blueprint('orgbp', __name__, url_prefix='/org')
#orgbp.route('/invite_user', methods=['GET','POST'])
#login_required
def invite_user():
[stuff goes here]
I would like to access an sqlite3 database from a Flask application (without using Flask-SQLAlchemy, since I require fts4 functionality). I am using Flask blueprints, and I am not sure where to put the following functions (shamelessly copied from a response to this stackoverflow question):
def request_has_connection():
return hasattr(flask.g, 'dbconn')
def get_request_connection():
if not request_has_connection():
flask.g.dbconn = sqlite3.connect(DATABASE)
# Do something to make this connection transactional.
# I'm not familiar enough with SQLite to know what that is.
return flask.g.dbconn
#app.teardown_request
def close_db_connection(ex):
if request_has_connection():
conn = get_request_connection()
# Rollback
# Alternatively, you could automatically commit if ex is None
# and rollback otherwise, but I question the wisdom
# of automatically committing.
conn.close()
My file structure is:
app
├── __init__.py
├── main
│ ├── forms.py
│ ├── __init__.py
│ ├── views.py
├── models.py
├── static
└── templates
├── base.html
├── index.html
└── login.html
I want the request_has_connection() and get_request_connection() functions accessible from all view functions and maybe from models.py as well. Right now, I'm thinking they all belong in my blueprint init.py, which currently contains:
from flask import Blueprint
main = Blueprint('main',__name__)
from . import views
and that my request teardown function would be registered as
#main.teardown_request
def close_db_connection(ex):
<blah-blah-blah>
Is this right?