How to trick and use a terrible library that is supposed to be kept globally in flask

Introduction

There are some libraries in flask that are supposed to be used globally as follows.

from flask import Flask
from something import Something


class settings:
    MESSAGE = "hello from something"

app = Flask(__name__)
app.config.from_object(settings)
hmm = Something(app)  #this


@app.route("/")
def hello():
    return hmm.hello()  #here


if __name__ == "__main__":
    app.run(port=4040)

Looking into the code inside, it seems that it does not substitute for app and takes app as an argument because it wants to get config information. Originally I don't want to use such a library. You may have to use it.

The implementation itself was as follows.

class Something(object):
    def __init__(self, app=None):
        self.init_app(app)

    def init_app(self, app):
        #Look at the config and do something
        self.message = app.config["MESSAGE"]

    def hello(self):  #The method you want to call
        return self.message

I don't want to put the app directly on global

I don't want to put the app directly on global. For example, I think it is normal to use blueprint for the definition of view. However, if you rewrite it as follows, a problem will occur.

views.py

from flask import Blueprint
b = Blueprint("hello", __name__)


@b.route("/")
def hello():
    return hmm.hello()  #I want to call this

app.py

def make_app():

    class settings:
        MESSAGE = "hello from something"

    app = Flask(__name__)
    app.config.from_object(settings)
    hmm = Something(app)  #There is no way to touch this
    app.register_blueprint(b)

    return app


if __name__ == "__main__":
    app = make_app()
    app.run(port=4040)

If you wrap the generation of the app in a function, there is no way to access the hmm you want to use. On the other hand, if you try to return hmm as the return value of make_app, it will be in the same state as the global variable, and it will be overwhelming.

thread local object

It seems to be the culture of flask to use thread local object. For example, the request object is thread local. It may be better to do it in a way that follows this. By the way, if you want to make it thread local, you can do as follows. current_app and g are also thread local.

from flask import g, current_app
from werkzeug.local import LocalProxy

def find_hmm():
    print("hoi")
    if not hasattr(g, "hmm"):
        print("hai")
        g.hmm = Something(current_app)
    return g.hmm

hmm = LocalProxy(find_hmm)

It can be shared between 1 requests. Of course, it will be regenerated every time a new request comes in. You may not like it.

If you make a request to http: // localhost: 4040 / twice, it will be as follows.

hoi
hai
hoi
hai

Interface similar to thread local object

You may really want to have one singleton. It seems to be the culture of flask to publish a global proxy, so let's create an object with a similar interface following that.

class LazyOnceEvalObject(object):
    def __init__(self, fn):
        self._fn = fn
        self.proxy = None

    def __getattr__(self, name):
        if self.proxy is None:
            self.proxy = self._fn()
        print("hai")
        return getattr(self.proxy, name)

def find_hmm():
    print("hoi")
    return Something(current_app)

hmm = LazyOnceEvalObject(find_hmm)

Only on the first request, find_hmm () will generate hmm.

If you make a request to http: // localhost: 4040 / twice, it will be as follows.

hoi
hai
hai

When the initialization process takes time

The initialization process may take some time. It may be too burdensome to initialize the hmm proxy at the time of request (albeit only for the first time). In such a case, it may be better to forcibly create an application context and set it.

def make_app():

    class settings:
        MESSAGE = "hello from something"

    app = Flask(__name__)
    app.config.from_object(settings)
    app.register_blueprint(b)

    with app.app_context():
        hmm.hello()
    return app

It may be easier to explicitly create a context so that it can be retrieved with current_app, rather than trying hard to pass the app.

Bonus story

Creating a new context may also be useful during testing. For example, if you execute the following code, after entering a value in g.foo in f0, f0 is called after creating a new context with app_context (), so it will be None in the second f1. ..

def f0():
    g.foo = "foo"
    print("f0 before with")
    f1()
    with current_app.app_context():
        print("f0 after with")
        f1()


def f1():
    print(getattr(g, "foo", None))


with app.app_context():
    f0()

result

f0 before with
foo
f0 after with
None

Recommended Posts

How to trick and use a terrible library that is supposed to be kept globally in flask
How to use a library that is not originally included in Google App Engine
How to use is and == in Python
How to use hmmlearn, a Python library that realizes hidden Markov models
How to use the C library in Python
How to use Python Image Library in python3 series
Tips for those who are wondering how to use is and == in Python
How to install a Python library that can be used by pharmaceutical companies
A memorandum on how to use keras.preprocessing.image in Keras
How to use any or all to check if it is in a dictionary (Hash)
Notes on how to use StatsModels that can use linear regression and GLM in python
How to use Docker to containerize your application and how to use Docker Compose to run your application in a development environment
[C / C ++] Pass the value calculated in C / C ++ to a python function to execute the process, and use that value in C / C ++.
How to set up a simple SMTP server that can be tested locally in Python
[Python] What is a tuple? Explains how to use without tuples and how to use it with examples.
It may be a problem to use Japanese for folder names and notebook names in Databricks
How to test that Exception is raised in python unittest
How to use the __call__ method in a Python class
How to use pyenv and pyenv-virtualenv in your own way
[Introduction to Udemy Python 3 + Application] 36. How to use In and Not
How to create and use static / dynamic libraries in C
Comparison of how to use higher-order functions in Python 2 and 3
How to write a metaclass that supports both python2 and python3
Notes on how to use marshmallow in the schema library
How to compare lists and retrieve common elements in a list
Use networkx, a library that handles graphs in python (Part 2: Tutorial)
How to give and what the constraints option in scipy.optimize.minimize is
How to use functions in separate files Perl and Python versions
How to judge that the cross key is input in Python3
How to use the asterisk (*) in Python. Maybe this is all? ..
[For beginners] How to register a library created in Python in PyPI
Is R's do.call () a classical higher-order function? Learn how to use
[Ln] How to paste a symbolic link in a directory is complicated
How to use classes in Theano
How to use .bash_profile and .bashrc
How to install and use Graphviz
How to use Mysql in python
How to use ChemSpider in Python
How to use PubChem in Python
How to input a character string in Python and output it as it is or in the opposite direction.
CNN determines which college a beautiful woman is likely to be in
How to get a specific column name and index name in pandas DataFrame
How to make a rock-paper-scissors bot that can be easily moved (commentary)
How to put a half-width space before letters and numbers in Python.
How to make a container name a subdomain and make it accessible in Docker
What is a C language library? What is the information that is open to the public?
It is convenient to use Layers when putting a library on Lambda
How to stop a program in python until a specific date and time
How to save the feature point information of an image in a file and use it for matching
I thought it would be slow to use a for statement in NumPy, but that wasn't the case.
How to use calculated columns in CASTable
[Introduction to Python] How to use class in Python?
How to split and save a DataFrame
How to install and use pandas_datareader [Python]
[Pandas] What is set_option [How to use]
How to use Google Test in C
How to get a stacktrace in python
Minimum knowledge to use Form in Flask
How to use Anaconda interpreter in PyCharm
python: How to use locals () and globals ()
How to use __slots__ in Python class