I was confused at one point when I was developing an application to run on wsgi - more specifically on uwsgi.
After building my example application:
class MyCustomApp():
def __call__(self, environ, start_response):
start_response('200 OK', [('Content-Type','application/json')])
return "a".encode('utf-8')
application = MyCustomApp()
Everything works perfectly as expected.
I use a class instead of a method, I need to use it for other reasons.
Now let's get to the problem. I am using middleware called beaker and if I replace my application with:
application = SessionMiddleware(MyCustomApp(),options)
Everything is normal, but I don't want to modify my call, despite for the sake of study and understanding.
I would like to do the following:
class MyCustomApp():
def __call__(self, environ, start_response):
start_response('200 OK', [('Content-Type','application/json')])
...
here i want to implement SessionMiddleware without modify
application = MyCustomApp()
...
return "a".encode('utf-8')
application = MyCustomApp()
But nothing I try, my middleware replaces my environment with its defaults. I wish they could guide me to understand and try to implement keeping the logic above.
Related
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.
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):
I am trying to abstract away some of the route class logic (i.e. I am looking to dynamically generate routes). api.add_resource seemed like the right place to do this.
So this is what I am trying to do:
# app.py
from flask import Flask
from flask_restplus import Api, Resource, fields
from mylib import MyPost
# Define my model
json_model = api.schema_model(...)
api.add_resource(
MyPost,
'/acme',
resource_class_kwargs={"json_model": json_model}
)
And then in mylib:
# mylib.py
def validate_endpoint(f):
def wrapper(*args, **kwargs):
return api.expect(json_fprint)(f(*args, **kwargs))
return wrapper
class MyPost(Resource):
def __init__(self, *args, **kwargs):
# Passed in via api.add_resource
self.api = args[0]
self.json_model = kwargs['json_model']
# I can't do this because I don't have access to 'api' here...
# #api.expect(json_model)
# So I am trying to make this work
#validate_endpoint
def post(self):
return {"data":'some data'}, 200
I don’t have access to the global api object here so I can’t call #api.expect(json_model). But I do have access to api and json_model inside of the post method. Which is why I am trying to create my own validate_endpoint decorator.
This does not work though. Is what I am trying to do here even possible? Is there a better approach I should be taking?
Stop using flask-restplus. Thats the most valuable answer I can give you (and anyone else).
Ownership is not there
Flask-restplus is a fork of flask-restful. Some engineers started developing features that suited them. The core guy has ghosted the project so its been officially forked again as Flask-Restx.
Poorly designed
I used to love flask when I was a yout’. I’ve realized since then that having global request, application, config that all magically update is not a good design. Their application factory pattern (to which flask-restplus conforms) is a style of statefully mutating the application object. First of all, Its hard to test. Second of all, it means that flask-restplus is wrapping the app and therefore all of the requests/handlers. How can anyone thing thats a good thing? A library whose main feature is endpoint documentation has its filthy hands all over every one of my requests?? (btw, this is whats leading to your problem above) Because my post is serious and thoughtful I’m skipping my thoughts on the Resource class pattern as it would probably push me into the waters of ranting.
Random Feature Set
A good library has a single purpose and it does that single thing well. Flask-restplus does 15 things (masking, swagger generation, postman generation, marshaling, request arg validation). Some features you can’t even tell are in the libraries code by reading the docs.
My solution to your problem
If you want to document your code via function decorators and models use a tool that does that alone and does it well. Use one that won’t touch your handlers or effect your actual request decorators. Use oapispec for swagger generation. For the other features of flask-restplus you’ve got marshmallow for marshaling request/response data, pydantic for validating request objects and args, and so on.
btw, I know all this because I had to build an api with it. After weeks of fighting the framework I forked it, ripped it apart, created oapispec, and trashed it from the project.
I'm trying to use the wonder apscheduler in a pyarmid api. The idea is to have a background job run regularly, while we still query the api for the result from time to time. Basically I use the job in a class as:
def my_class(object):
def __init__(self):
self.current_result = 0
scheduler = BackGroundScheduler()
scheduler.start()
scheduler.add_job(my_job,"interval", id="foo", seconds=5)
def my_job():
print("i'm updating result")
self.current_result += 1
And outside of this class (a service for me), the api has a POST endpoint that returns my_class instance's current result:
class MyApi(object):
def __init__(self):
self.my_class = MyClass()
#view_config(request_method='POST')
def my_post(self):
return self.my_class.current_result
When everything runs, I see the prints and incrementation of value inside the service. But current_result stays as 0 when gathered from the post.
From what I know of the threading, I guess that the update I do is not on the same object my_class but must be on a copy passed to the thread.
One solution I see would be to update the variable in a shared intermediate (write on disk, or in a databse). But I wondered if that would be possible to do in memory.
I manage to do exactly this when I do this in a regular script, or with one script and a very simple FLASK api (no class for the API there) but I can't manage to have this logic function inside the pyramid api.
It must be linked to some internal of Pyramid spawning my api endpoint on a different thread but I can't get right on the problem.
Thanks !
=== EDIT ===
I have tried several things to solve the issue. First, the instance of MyClass used is intitialized in another script, follow a container pattern. That container is by default contained in all MyApi instances of pyramid, and supposed to contain all global variables linked to my project.
I also define a global instance of MyClass just to be sure, and print its current result value to compare
global_my_class = MyClass()
class MyApi(object):
def __init__(self):
pass
#view_config(request_method='POST')
def my_post(self):
print(global_my_class.current_result)
return self.container.my_class.current_result
I check using debug that MyClass is only spawned twice during the api execution (one for the global variable, one inside the container. However.
So what I see in logging are two values of current_result getting incremented, but at each calls of my_post I only get 0s.
An instance of view class only lives for the duration of the request - request comes in, a view class is created, produces the result and is disposed. As such, each instance of your view gets a new copy of MyClass() which is separate from the previous requests.
As a very simple solution you may try defining a global instance which will be shared process-wide:
my_class = MyClass()
class MyApi(object):
#view_config(request_method='POST')
def my_post(self):
return my_class.current_result
I'm trying to test some routes on my Flask app that call external APIs, which I want to mock.
The routes are set up like this:
#app.route('/url/<string::arg>')
def route_function(arg):
data = external_api(arg)
response = make_response(data)
# configure response
return response
I originally tried something like this:
class TestFlaskApp(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
#patch('external_api',
side_effect=mock_api)
def test_flask_route(self, api):
result = app.get('/url/arg')
self.assertEqual(result.status_code, 200)
api.assert_called_once_with('arg')
...which failed. The mock API function wasn't called, since I assume the mock does not apply in the app context.
I also tried this, thinking I might be able to test the route functions directly and thus avoid having to use the app context:
class TestAppFunctions(unittest.TestCase):
#patch('external_api',
side_effect=mock_api)
def test_flask_function(self, api):
result = my_flask_app.route_function('arg')
self.assertEqual(result.status_code, 200)
api.assert_called_once_with('arg')
...but this didn't work either, since to make a response, route_function needs the app context.
So is there a way to mock inside the app context? How else can I test these routes without triggering external API calls?
Oluwafemi Sule was right...I just needed to patch the function where it was used, not where it was defined.
You need to pass the object path to the patch function so that it can be resolved and replaced with the mock at runtime. For example if external_api function is called in a module named routes which is in turn contained in a package named my_shining_app, patch will be passed as my_shining_app.routes.external_api
Note that the path should be where the function is called (i.e. where it's to be replaced with the mock) and not where it's defined