Django viewflow: not receiving flow_finished signal - django-viewflow

I'm trying to catch the flow_finished signal from django viewflow like this
flow_finished.connect(function)
but it's not working. The function isn't called even if the flow finishes.
Any help please, I'm pretty lost.
In my app's init.py I added this
from django.apps import AppConfig
default_app_config = 'test.TestConfig'
class TestConfig(AppConfig):
name = 'test'
verbose_name = 'Test'
def ready(self):
import viewflow.signals

First, you need to ensure that you properly configured you app config, and the ready method really been called. Check your installed apps that you properly included your TestConfig, or if you use shortcuts, check you test/__init__.py default_app_config value
from viewflow.signals import flow_finished
def receiver(sender, **kwargs):
print('hi')
class TestConfig(AppConfig):
name = 'test'
def ready(self):
flow_finished.connect(receiver)
But generally, using signals to weave your codebase is a bad taste. To call an action before flow.End you can explicitly add flow.Handler. That's the recommended solution.

Related

Class wide mock in pytest (for all methods in the whole TestClass)

I am unittesting my new librabry, which is basically database interface. Our apps use it to access our database. that means, I want to test all methods, but I do not want DB commands to be called for real. I only check if they are called with correct arguemnts.
For that purpose, I am mocking the database library. this is the actual code that DOES work:
import pytest
from unittests.conftest import PyTestConfig as Tconf
from my_lib.influx_interface import InfluxInterface
class TestInfluxInterface:
def test_connect(self, mocker):
"""
Create InfluxConnector object and call connect()
check if __init__() arguments are passed / used correctly
"""
influx_client = mocker.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
test_connector = InfluxInterface(Tconf.test_id)
# Call connect with no input (influx_client - should be called with no arguemnts
test_connector.connect()
influx_client.assert_called_once()
influx_client.reset_mock()
# Call connect with custom correct input (influx_client - should be called with custom values
test_connector.connect(Tconf.custom_conf)
influx_client.assert_called_once_with(url=Tconf.custom_conf["url"], token=Tconf.custom_conf["token"],
org=Tconf.custom_conf["org"], timeout=Tconf.custom_conf["timeout"],
debug=Tconf.custom_conf["debug"])
influx_client.reset_mock()
# Call connect with incorrect input (influx_client - should be called with default values
test_connector.connect(Tconf.default_conf)
influx_client.assert_called_once_with(url=Tconf.default_conf["url"], token=Tconf.default_conf["token"],
org=Tconf.default_conf["org"], timeout=Tconf.default_conf["timeout"],
debug=Tconf.default_conf["debug"])
Now, what I do next, is to add more methods into TestInfluxInterface class, which will be testing rest of the code. One test method for each method in my library. Thats how I usually do it.
The problem is, that there is a part of the code:
influx_client = mocker.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
test_connector = InfluxInterface(Tconf.test_id)
That will be same for every method. Thus I will be copy-pasting it over and over. As you can already see, thats not good solution.
In unittest, I would do this:
import unittest
import unittest.mock as mock
from unittests.conftest import PyTestConfig as Tconf
from my_lib.influx_interface import InfluxInterface
#mock.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
class TestInfluxInterface:
def setUp(self):
self.test_connector = InfluxInterface(Tconf.test_id)
def test_connect(self, influx_client):
"""
Create InfluxConnector object and call connect()
check if __init__() arguments are passed / used correctly
"""
# Call connect with no input (influx_client - should be called with no arguemnts
self.test_connector.connect()
influx_client.assert_called_once()
Than in each method, I would use self.test_connector to call whatever method I want to test, and check if it called correct influx_client method, with correct parameters.
However, we are moving from unittest to pytest. And while I am wrapping my head around pytest docs, reading stuff here and there, fixtures, mockers, etc..I cannot find out how to do this correctly.

Run Flask on demand to visualize computed data

Using Python 3.7, I made a CLI utility which prints some results to stdout. Depending on an option the results should be visualized in a browser (single user, no sessions). Flask seems to be a good choice for this. However, this is not a standard usecase described in the docs or in tutorials.
I am looking for a best practise way to pass the data (e.g. a Python List) to the Flask app so that I can return it from view functions. Basically it would be immutable application data. The following seems to work but I don't like to use globals:
main.py:
import myapp
result = compute_stuff()
if show_in_browser:
myapp.data = result
myapp.app.run()
myapp.py:
from flask import Flask
from typing import List
app = Flask(__name__)
result: List
#app.route("/")
def home():
return f"items: {len(result)}"
Reading the Flask docs I get the impression I should use an application context. On the other hand, its lifetime does not span across requests and I would not know how to populate it. Reading other questions I might use a Flask config object because it seems to be available on every request. But this is not really about configuration. Or maybe I should use Klein, inspired by this answer?
There does not seem to be a best practice way. So I am going with a modification of my original approach:
class MyData:
pass
class MyApp(Flask):
def __init__(self) -> None:
super().__init__(__name__)
self.env = "development"
self.debug = True
def getData(self) -> MyData:
return self._my_data
def setMyData(self, my_data: MyData) -> None:
self._my_data = my_data
app = MyApp()
This way I can set the data after the app instance was already created - which is necessary to be able to use it in routing decorators defined outside of the class. It would be nice to have more encapsulation: use app methods for routing (with decorators) instead of module global functions accessing a module global app object. Apparently that is not flaskic.

CherryPy Tool: How to register custom tool correctly?

I want to create a simple tool but fail to register it correctly. As soon as I add it to any method I get the error:
AttributeError: 'Toolbox' object has no attribute 'authenticate'
I tried
cherrypy.tools.authenticate = cherrypy.Tool('before_handler', authenticate)
and
#cherrypy.tools.register('before_handler')
def authenticate():
The issue I likely have is placing the function in the wrong place. I have a main file launching the server and all apps:
#config stuff
if __name__ == '__main__':
cherrypy.engine.unsubscribe('graceful', cherrypy.log.reopen_files)
logging.config.dictConfig(LOG_CONF)
cherrypy.tree.mount(app1(), '/app1')
cherrypy.tree.mount(app2(), '/app2')
cherrypy.quickstart(app3)
This file is launched by a systemd unit.
If I put the authenticate function in the config area, it doesn't work. If i put it in one of the apps directly and only use it in that app, it doesn't work. Always the same error.
So where do I have to place it to make this work?
Another case of me falling into the python definition order matters trap.
Doesn't work:
class MyApp(object):
#....
#cherrypy.tools.register('on_start_resource')
def authenticate():
#....
Works:
#cherrypy.tools.register('on_start_resource')
def authenticate():
#....
class MyApp(object):

how can i create a common instance of an utility class in Flask in python?

I have a Flask app with multiple routes. I have defined an Utility class with few methods to handle the common functionalities, for eg. getting some properties etc. Now I want to create and instance of the Utility class in a common place to be used by functions in each route. How do I achieve this ?
from flask import Flask, request, jsonify
import mysql.connector as mariadb
app = Flask(__name__)
#app.route('/aaa/bbb/ccc',methods=['POST'])
def func1():
pass
#app.route('/xxx/yyy/zzz',methods=['POST'])
def func2():
pass
#app.route('/abc/dfg/ijk',methods=['POST'])
def func3():
pass
class Utility:
def get_properties():
pass
def get_database_conn():
pass
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5000, debug=True)
Now, I want to do something like -
util_obj = Utility()
in the beginning of the app, so that from any of my routes I can call the utility methods without having to create the object every time.
What is the best way to achieve this ?
What you're describing seems to follow the same structure as most extensions to Flask, like Flask-SQLAlchemy or Flask-Caching, namely to have a globally configured instance that you import or call upon when necessary.
In a large scale application, you would create these instances in a file like extensions.py, away from where you create app to avoid circular imports, but in the case of a single file, you don't have to worry about that. Instead, I would simply move the class definition of Utility to just underneath where you define app, and create util_obj right below the class definition.
from flask import Flask, request, jsonify
import mysql.connector as mariadb
app = Flask(__name__)
class Utility:
def get_properties():
# you can access app here!
pass
def get_database_conn():
pass
util_obj = Utility()
#app.route('/aaa/bbb/ccc',methods=['POST'])
def func1():
# you can access util_obj here!
pass
#app.route('/xxx/yyy/zzz',methods=['POST'])
def func2():
pass
#app.route('/abc/dfg/ijk',methods=['POST'])
def func3():
pass
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5000, debug=True)

Creating custom 'test' command to run a test suite for a Flask application

We are extending the Flask-cli with some custom commands. The command test is one of them:
# run.py: specified by FLASK_APP
# This is the Flask application object
app = create_app(os.getenv('FLASK_ENV') or 'default')
#app.cli.command()
def test():
"""Run the unit tests."""
tests = unittest.TestLoader().discover('tests')
test_runner = unittest.TextTestRunner()
test_runner.run(tests)
However a typical test (using Python's built-in unittest module) looks like
this which is based on the style described here.
# some-tests.py: unittest-based test case.
class SomeTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app('testing')
self.app_context = self.app.app_context()
self.app_context.push()
def tearDown(self):
self.app_context.pop()
def test_load(self):
pass
I am clearly hitting an anti-pattern here: I have initialized a flask object with the default(development) configuration because I need it for the #app.cli.command() decorator which all happens in run.py. However once I run the test setUp function in some-tests.py I somehow have to obtain a Flask object utilizing the testing configuration, e.g. by recreating a Flask app with the testing configuration like what happens now.
I would like to have pointers on how one goes about to implement a flask-cli test command in which only one Flask object is created which is reused amongst the various test cases without having the need of explicitely setting the environment to testing before I run flask test on the command line.
I'm not sure if this answer will suit your requirements but that is how I would try to approach this problem. Unfortunately, if you want to use default CLI interface in Flask than you need to call create_app just to call flask test command. What you can do is try use pytest. It allows you to create fixtures that can be used across multiple test cases. For example, in your tests package create file named conftest.py and declare some default fixtures like this:
#pytest.fixture
def app():
return create_app('testing')
#pytest.fixture
def client(app):
return app.test_client()
#pytest.fixture
def database(app):
_db.app = app
with app.app_context():
_db.create_all()
yield _db
_db.session.close()
_db.drop_all()
Then in your test case file (ex. test_login.py) you can use those fixtures like this:
# Notice argument names are the same as name of our fixtures
# You don't need to import fixtures to this file - pytest will
# automatically recognize fixtures for you
def test_registration(app, client):
response = client.post(
'/api/auth/login',
json={
'username': 'user1',
'password': '$dwq3&jNYGu'
})
assert response.status_code == 200
json_data = response.get_json()
assert json_data['access_token']
assert json_data['refresh_token']
The best thing about this approach is that you don't need to create setUp and tearDown methods. Then you can create test cli command for your app:
import pytest
#app.cli.command()
def test():
'''
Run tests.
'''
pytest.main(['--rootdir', './tests'])
And call it like this flask test.

Resources