Flask testing with pytest, ENV is set to production? - python

I have built a flask app and i would like to create a test suite. Reading around it looks like pytest is the way to go; however, I am finding it very difficult to understand how to get going, I have looked at https://flask.palletsprojects.com/en/2.0.x/testing/ but am struggling to relate it to my app.
my project has a run.py at its base:
from wahoo_connect import init_app, db
from flask_migrate import Migrate
#run the app
app = init_app()
migrate = Migrate(app, db)
this is run using flask run and the .flaskenv sets the mode
FLASK_APP=run.py
#FLASK_ENV=production
FLASK_ENV=development
I have an application factory set up:
"""Main entry point into App"""
#import libraries
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_mail import Mail
#from flask_caching import Cache
from flask_session import Session
from os import path, mkdir
import logging
from logging.handlers import SMTPHandler, RotatingFileHandler
"""Flask Global Variables"""
#database connection
db = SQLAlchemy()
migrate = Migrate()
#login manager
login = LoginManager()
login.login_view = 'auth_bp.login'
login.login_message = 'Please log in to access this page.'
login.login_message_category = "info"
#email
mail = Mail()
#cache
#cache = Cache()
#session
sess = Session()
#initialise app
def init_app():
"""Initialize the core application."""
app = Flask(__name__)
print(app.config['ENV'])
if app.config['ENV'] == 'production':
app.config.from_object('wahoo_connect.config.ProductionConfig')
elif app.config['ENV'] == 'development':
app.config.from_object('wahoo_connect.config.DevelopmentConfig')
elif app.config['ENV'] == 'testing':
app.config.from_object('wahoo_connect.config.TestingConfig')
# Initialize Plugins
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
# cache.init_app(app)
sess.init_app(app)
with app.app_context():
# Import and Register Blueprints
from wahoo_connect.errors.views import errors_bp
from wahoo_connect.auth.views import auth_bp
from wahoo_connect.profile.views import profile_bp
from wahoo_connect.wahoo.views import wahoo_bp
from wahoo_connect.home.views import home_bp
app.register_blueprint(errors_bp)
app.register_blueprint(auth_bp, url_prefix='/auth')
app.register_blueprint(profile_bp)
app.register_blueprint(wahoo_bp, url_prefix='/wahoo')
app.register_blueprint(home_bp)
if not app.debug:
#log to email
if app.config['MAIL_SERVER']:
auth = None
if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']:
auth = (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD'])
secure = None
if app.config['MAIL_USE_TLS']:
secure = ()
mail_handler = SMTPHandler(
mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr=app.config['MAIL_USERNAME'],
toaddrs=app.config['ADMINS'],
subject='Error in Wahoo-Connect',
credentials=auth, secure=secure)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
#log to file - heroku
if app.config['LOG_TO_STDOUT']:
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
app.logger.addHandler(stream_handler)
else:
logdir = path.dirname(app.instance_path) + '/logs'
if not path.exists(logdir):
mkdir(logdir)
file_handler = RotatingFileHandler(logdir + '/wahoo-connect.log',
maxBytes=10240, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Wahoo-Connect Startup')
return app
from wahoo_connect import models
Everything works and I can run the app on a production server. I am trying to get started with pytest and have set up conftest.py:
import pytest
from wahoo_connect import init_app
from wahoo_connect.models import User
#pytest.fixture(scope="session")
def app():
app = init_app()
return app
this always runs the app in production mode, how do I get it to run in testing mode so that it uses the correct config from config.py
"""Flask configuration."""
from os import environ, path
from dotenv import load_dotenv
from datetime import timedelta
#basedir = path.abspath(path.dirname(__file__))
basedir = path.dirname(path.dirname(path.abspath(__file__)))
load_dotenv(path.join(basedir, '.env'))
class Config(object):
"""Base config."""
DEBUG = False
TESTING = False
SECRET_KEY = environ.get('SECRET_KEY') or 'topsecretkey'
STATIC_FOLDER = 'static'
TEMPLATES_FOLDER = 'templates'
SESSION_COOKIE_SECURE = True
#Database
SQLALCHEMY_DATABASE_URI = environ.get('DATABASE_URL') or \
'sqlite:///' + path.join(basedir, 'app.db')
#fix for heroku postgres db
if SQLALCHEMY_DATABASE_URI.startswith("postgres://"):
SQLALCHEMY_DATABASE_URI = SQLALCHEMY_DATABASE_URI.replace("postgres://", "postgresql://", 1)
SQLALCHEMY_TRACK_MODIFICATIONS = False
#Wahoo
WAHOO_CLIENT_ID=environ.get('WAHOO_CLIENT_ID')
WAHOO_CLIENT_SECRET=environ.get('WAHOO_CLIENT_SECRET')
WAHOO_CLIENT_REDIRECT=environ.get('WAHOO_CLIENT_REDIRECT')
#Email
MAIL_SERVER = environ.get('MAIL_SMTP_SERVER')
MAIL_PORT = int(environ.get('MAIL_SMTP_PORT') or 25)
MAIL_USERNAME = environ.get('MAIL_SMTP_LOGIN')
MAIL_PASSWORD = environ.get('MAIL_SMTP_PASSWORD')
MAIL_USE_TLS = environ.get('MAIL_USE_TLS') is not None
ADMINS = ['martyndwheeler#gmail.com']
#Logging
LOG_TO_STDOUT = environ.get('LOG_TO_STDOUT') or None
# Flask-Caching related configs
CACHE_TYPE = 'FileSystemCache'
CACHE_DIR = environ.get('CACHE_DIR') or None
CACHE_DEFAULT_TIMEOUT = 300
CACHE_THRESHOLD = 100
# Flask-Session related configs
SESSION_PERMANENT = False
PERMANENT_SESSION_LIFETIME = timedelta(minutes=30)
SESSION_USE_SIGNER = True
SESSION_TYPE = "filesystem"
SESSION_FILE_DIR = environ.get('SESSION_FILE_DIR')
SESSION_FILE_THRESHOLD = 100
class ProductionConfig(Config):
pass
class DevelopmentConfig(Config):
DEBUG = True
SESSION_COOKIE_SECURE = False
class TestingConfig(Config):
TESTING = True
SESSION_COOKIE_SECURE = False
If there is a good tutorial that I have missed I'd be pleased to hear.

What is Dotenv
Use Dotenv package
#test.py
from os import environ
print(environ.get("ENV_VAR")) # Output : test
#.env
ENV_VAR=test
How to use it in youre case
Option 1
You can use this to store an Boolean in .env file and read it to define wich mode you are running. be careful as env variables get read as string. if you wanna use boolean you need to parse them from the string.
Option 2
An other option is to store in env file which config you wanna use and make an if else tree in python script:
if os.environ.get("CONFIG") == "1":
app.config.from_object("config.conf1")
elif os.environ.get("CONFIG") == "2":
app.config.from_object("config.conf2")
else:
app.config.from_object("config.default")
Why use Dotenv
The advantage of using environment variables is that you can ignore them in git and every time the server is seetup in linux or docker all settings can be managed from one file. also does the standart server admin know bash script and env files but not necesseraly python.

Related

Using data from python Web-socket inside flask application

I came across a problem where I want to consume data from a python Websocket named truedata-ws inside my flask application, I had tried different ways but, I am not able to succeed. Please help me out for the same below are my main script file and the file for the connection to the Websocket
Main.py file
import sqlalchemy
from flask import Flask, url_for
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
from flask_mail import Mail
from flask_restful import Api
from flask_sock import Sock
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from ryj_bhav.Config import Config
from truedata_ws.websocket.TD import TD
import logging
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = 'users.login'
login_manager.login_message_category = 'info'
db = SQLAlchemy()
mail = Mail()
api = Api()
sock = Sock()
Gold_Onn = 55000
Silver_Onn = 65000
#sock.route('/echo')
def echo():
while True:
username = 'test'
password = 'test'
realtime_port = 8084
url = 'push.truedata.in'
symbols = ["SYMBOL-I", "SYMBOL-II"]
td_obj = TD(username, password, live_port=realtime_port, url=url, log_level=logging.DEBUG,
log_format="%(message)s")
#td_obj.trade_callback
def strategy_callback(symbol_id, tick_data):
print(f'Trade update > {tick_data}')
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
bcrypt.init_app(app)
login_manager.init_app(app)
db.init_app(app)
mail.init_app(app)
sock.init_app(app)
from ryj_bhav.routes import routes
from ryj_bhav.routes import LiveRateAPI
api.add_resource(LiveRateAPI, '/live-bhav')
api.init_app(app)
app.register_blueprint(routes)
return app
true-data-ws.py (websocket file)
from truedata_ws.websocket.TD import TD
import time
import logging
from datetime import date
username = 'test'
password = 'test'
realtime_port = 8084
url = 'push.truedata.in'
symbols = ["SYMBOL-I", "SYMBOL-II"]
print('About To Start')
td_obj = TD(username, password, live_port=realtime_port, url=url, log_level=logging.DEBUG, log_format="%(message)s")
# td_obj = TD(username, password, live_port=realtime_port, url=url, historical_api=False)
print('\n')
print('Starting Real Time Feed....')
req_ids = td_obj.start_live_data(symbols)
live_data_objs = {}
time.sleep(2)
for req_id in req_ids:
print('my req ids', req_id)
print(f'touchlinedata -> {td_obj.touchline_data[req_id]}')
#td_obj.trade_callback
def strategy_callback(symbol_id, tick_data):
print('My Symbol id',tick_data.get('ltp'))
# print(f'Trade update > {tick_data}')
# #td_obj.bidask_callback
# def new_bidask(symbol_id, tick_data):
# print(f"BidAsk update > {tick_data}")
while True:
time.sleep(120)
I had tried this solution and also tried to run it from my main file
run.py
from ryj_bhav import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
I had also tried multithreading but did not got succeed in any of the above tried solution.

how to access app object in other files of flask app

# flask packages
import jwt
from flask import Flask
from flask_restful import Api
from flask_mongoengine import MongoEngine
from flask_jwt_extended import JWTManager
import logging
# local packages
import models
from api.routes import create_routes
# external packages
import os
# default mongodb configuration
default_config = {'MONGODB_SETTINGS': {
'db': 'blog_db',
'host': 'localhost',
'port': 27017}
}
def get_flask_app(config: dict = None):
"""
Initializes Flask app with given configuration.
Main entry point for wsgi (gunicorn) server.
:param config: Configuration dictionary
:return: app
"""
# init flask
app = Flask(__name__)
# # configure app
# config = default_config if config is None else config
# app.config.update(config)
# load config variables
if 'MONGODB_URI' in os.environ:
app.config['MONGODB_SETTINGS'] = {'host': os.environ['MONGODB_URI'], 'retryWrites': False}
if 'JWT_SECRET_KEY' in os.environ:
app.config['JWT_SECRET_KEY'] = os.environ['JWT_SECRET_KEY']
app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
# File upload configs
app.config['PROFILE_FOLDER'] = '/images/profiles'
app.config['PROFILE_FOLDER'] = '/images/posts'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.config['ALLOWED_EXTENSIONS'] = set(['png', 'jpg', 'jpeg', 'gif'])
# init api and routes
api = Api(app=app)
create_routes(api=api)
# init mongoengine
db = MongoEngine(app=app)
# init jwt manager
jwt = JWTManager(app=app)
# #jwt.token_in_blocklist_loader
# def check_if_token_in_blacklist(decrypted_token):
# jti = decrypted_token['jti']
# return models.RevokedTokens.is_jti_blacklisted(jti)
return app
if __name__ == '__main__':
# Main entry point when run in stand-alone mode.
app = get_flask_app()
app.run(debug=True)
I am trying to use app object in the file in different folder utilities.py
import os
import sys
from werkzeug.utils import secure_filename
from flask import jsonify
from run import app
class Utilities:
FILE_ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
#staticmethod
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in Utilities.FILE_ALLOWED_EXTENSIONS
#staticmethod
def upload_files(file, filename, foldername):
try:
print(">>>>>>>>>" + foldername + filename, file=sys.stdout)
filename = secure_filename(filename)
file.save(os.path.join(app.root_path, foldername, filename))
resp = jsonify({'message': 'File successfully uploaded'})
resp.status_code = 201
return resp
except Exception as e:
print(">>>>>>>>>" + str(e), file=sys.stdout)
resp = jsonify({'message': 'File upload failed'})
resp.status_code = 400
return resp
but getting below error :
ImportError: cannot import name 'create_routes' from partially initialized module 'api.routes' (most likely due to a circular import) (D:\Python\Projects\XXXX\api\routes.py)
enter image description here

"Oracle "alter session set" via flask-sqlalchemy and cx-oracle"

how can I start a connection to an oracle database using flask-sqlalchemy and cx-oracle by sending these parameters:
alter session set nls_comp=linguistic;
alter session set nls_sort=Latin_AI;
In order to be able to make queries and sorts that do not distinguish, for example, between "Jose" and "José"
That's how I usually make the connection:
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
def create_app(config_name=(os.getenv('FLASK_CONFIG') or 'default')):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
from commons import commons_bp
from admin import admin_bp
from inventario import inventario_bp
from incidencias import incidencias_bp
from inconformidades import inconformidades_bp
with app.app_context():
app.register_blueprint(commons_bp)
app.register_blueprint(admin_bp)
app.register_blueprint(inventario_bp)
app.register_blueprint(incidencias_bp)
app.register_blueprint(inconformidades_bp)
return app
and the configuration:
class DevelopmentConfig(Config):
TESTING = True
DEBUG = True
SQLALCHEMY_ECHO = os.environ.get('SQLALCHEMY_ECHO') or False
SQLALCHEMY_POOL_SIZE = None
SQLALCHEMY_POOL_TIMEOUT = None
SQLALCHEMY_BINDS = {
'adminapps': 'oracle://ADMINAPPS:ADMINAPPS#SERVIDOR53/ENTECO',
'incidencias': 'oracle://INCIDENCIAS:INCIDENCIAS#SERVIDOR53/ENTECO',
'incidencias': 'oracle://INCIDENCIAS:INCIDENCIAS#SERVIDOR53/ENTECO',
'inventario': 'oracle://INVENTARIO:INVENTARIO#SERVIDOR53/ENTECO',
'inconformidades': 'oracle://INCONFORMIDADES:INCONFORMIDADES#SERVIDOR53/ENTECO',
'documentacion': 'oracle://DOCUMENTACION:DOCUMENTACION#SERVIDOR53/ENTECO',
'auditoria': 'oracle://AUDITORIA:AUDITORIA#SERVIDOR53/ENTECO',
'navision': 'mssql+pyodbc://DB:DB.2020#SERVIDOR52',
}
I've tried among other things:
'adminapps': ['oracle://ADMINAPPS:ADMINAPPS#SERVIDOR53/ENTECO', 'alter session set "nls_comp"="linguistic"', 'alter session set "nls_sort"="Latin_AI"'],
But I haven't been successful, I can't figure out how to do it, thank you for your answers!

Flask - RuntimeError: No application found. Either work inside a view function or push an application context

I'm now trying to make an application that processes HTTP requests with REST APIs. The application uses Flask for the web framework, and it uses Celery for the asynchronous tasks.
Here's the application structure.
app.py
celery_app.py
controller/
controller.py
task/
task.py
(1) app.py
There are no lines for the Celery configuration.
from flask import Flask
...
app = Flask(__name__)
...
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
(2) celery.py
from consts.consts import Consts
from kombu.utils.url import quote
from celery import Celery
from config.config import Config
config = Config()
db_uri = 'db+' + config.get_db_uri()
aws_access_key = quote(config.get_config(Consts.AWS, Consts.AWS_ACCESS_KEY))
aws_secret_key = quote(config.get_config(Consts.AWS, Consts.AWS_SECRET_KEY))
broker_url = "sqs://{aws_access_key}:{aws_secret_key}#".format(
aws_access_key=aws_access_key, aws_secret_key=aws_secret_key
)
celery = Celery(
broker=broker_url,
backend=db_uri,
include=['task']
)
(3) task/task.py
I put all the tasks here.
from celery_app import celery
from flask import current_app as app
from model.model import db
#celery.task
def check_friend_status_from_db(user1, user2):
status = db.engine.execute(
"QUERY").fetchone()
return status
Now, the controller/controller.py file imports and calls the tasks as follows.
(4) controller/controller.py
from flask import Blueprint, request, json, render_template, jsonify
from mysql.connector import Error
from sqlalchemy import or_, and_
from consts.consts import StatusCode
from consts.consts import Consts
from model.model import db, FriendRequest
from outbound.request import Request
from util.logging import logger
from task.task import check_friend_status_from_db
controller_blueprint = Blueprint('controller_blueprint', __name__)
outbound_request = Request()
#controller_blueprint.route('/friend/status/<requested_from>/<requested_to>', methods=['GET'])
def check_friend_status(requested_from, requested_to):
logger.info('Checking the friend status')
try:
status = check_friend_status_from_db.apply_async((requested_from, requested_to)).get()
if status is None:
response = {
Consts.STATUS_CODE: StatusCode.OK,
Consts.FRIEND_STATUS: StatusCode.NO_RELATION
}
else:
response = {
Consts.STATUS_CODE: StatusCode.OK,
Consts.FRIEND_STATUS: status
}
except Error as e:
logger.error("TypeError:", e)
response = {
Consts.STATUS_CODE: StatusCode.ERROR
}
json_response = jsonify(response)
logger.info(json_response)
return json_response
When I run the code, I get the error as I mentioned on the title.
RuntimeError: No application found. Either work inside a view function or push an application context
and it turns out to be this part under the try block in the controller where the error is coming from.
status = check_friend_status_from_db.apply_async((requested_from, requested_to)).get()
Any solutions, please?
It looks like you need to register your blueprint controller_blueprint. Since this blueprint is not registered to your app, you are working outside the context of you application and hence the error.
You can do so in your app.py:
from controller.controller import controller_blueprint
app = Flask(__name__)
app.register_blueprint(controller_blueprint)

How to get secret key in process of connecting flask app to database

I m following a tutorial on making a database connection between Flask and PostgreSQL using json, and there is a secret key, mentioned in config.py
I have read some other answers and understood Flask uses urandom to generate a random key. But not exactly clear at which moment of time i must run this code to generate the secret key. I do understand that this code must be run on command prompt.
python
>>> import os
>>> os.urandom(24)
My config.py code
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
DEBUG = False
TESTING = False
CSRF_ENABLED = True
SECRET_KEY = 'this-really-needs-to-be-changed'
SQLALCHEMY_DATABASE_URI = os.environ['postgresql://postgresql:silverTip#localhost/DatabaseFirst']
class ProductionConfig(Config):
DEBUG = False
class StagingConfig(Config):
DEVELOPMENT = True
DEBUG = True
class DevelopmentConfig(Config):
DEVELOPMENT = True
DEBUG = True
class TestingConfig(Config):
TESTING = True
Run that code in a python shell:
>>> import os
>>> os.urandom(24)
b'\x1d\xc6\x0f[\xed\x18\xd6:5\xe0\x0f\rG\xaf\xb4\xf4HT\xef\xc1\xf1\xa89f'
Then copy/paste the result into your config file:
SECRET_KEY = '\x1d\xc6\x0f[\xed\x18\xd6:5\xe0\x0f\rG\xaf\xb4\xf4HT\xef\xc1\xf1\xa89f'
Remember to remove the leading b, otherwise you will saving the SECRET_KEY as a bytes object, rather than a string.

Categories

Resources