I'm trying to use Flask Restful and Flask SocketIO at the same time. I already made a Flask Restful application but now I want to use Flask SocketIO to have realtime communication between client and my server.
from flask import Flask
from flask_restful import Api
from flask_socketio import SocketIO
app = Flask(__name__)
api = Api(app)
socketio = SocketIO()
if __name__ == '__main__':
socketio.run(app, port=5000, host='0.0.0.0')
app.run(port=5000, host='0.0.0.0')
Once I run this, I get
Traceback (most recent call last):
File "app.py", line 10, in <module>
socketio.run(app, port=5000, host='0.0.0.0')
File "C:\Python27\lib\site-packages\flask_socketio\__init__.py", line 475, in run
if self.server.eio.async_mode == 'threading':
AttributeError: 'NoneType' object has no attribute 'eio'
I'm a beginner in coding with Flask. Hope you could help me. Thanks.
Flask-Restful does not change anything with regards to how you start your server. You can do:
app = Flask(__name__)
api = Api(app)
socketio = SocketIO(app)
if __name__ == '__main__':
socketio.run(app, port=5000, host='0.0.0.0')
The code you pasted in your question had a bug that was causing the AttributeError, and that was that you were not passing the app instance to the SocketIO constructor.
Related
I want to write a minimal FastAPI static file server launched from a script that allows you to specify the directory to share on the command line. Following the example in the FastAPI documentation, I wrote this.
import uvicorn
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
server = FastAPI()
if __name__ == "__main__":
import sys
directory = sys.argv[1]
server.mount("/static", StaticFiles(directory=directory), name="static")
uvicorn.run(app="my_package:server")
If I run this with the argument /my/directory where this directory contains file.txt I expect that I'd be able to download file.txt at the URL http://localhost:8000/static/file.txt, but this returns an HTTP 404.
How do I write this minimal static file server script?
The assumption I made about sys.argv not being available when uvicorn loads your module is wrong, so it should work as you expect by moving your static setup outside of the __main__ guard:
import uvicorn
import sys
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
server = FastAPI()
directory = sys.argv[1]
server.mount("/static", StaticFiles(directory=directory), name="static")
if __name__ == "__main__":
uvicorn.run(app="my_package:server")
When you call uvicorn.run(app="my_package:server"), it actually starts a separate process where my_package is imported. Therefore, everything inside if __name__ == "__main__": will not be run in the uvicorn process, so your directory will never be mounted.
One possible solution would be getting the directory from an environment variable, which is set from a small bash script:
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
server = FastAPI()
directory = os.getenv("DIRECTORY")
server.mount("/static", StaticFiles(directory=directory), name="static")
start.sh:
#!/usr/bin/env bash
DIRECTORY=$1 uvicorn mypackage:server
I create a basic upload example as below. It works fine if the file size is less than 1MB, but got a 400 bad request, "detail": "There was an error parsing the body" once the file size is larger than 1MB. I have installed the python-multipart package.
from typing import List
import uvicorn
from fastapi import FastAPI, UploadFile, File
app = FastAPI(debug=True)
#app.post("/uploadfiles/")
async def create_upload_files(file: UploadFile = File(...)):
return {"filenames": file.filename}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
the problem is the version of anyio python package. Consider using versions >= 3.4.0.
I have an app.py file where I initialize my app. I have another file (run.py) where I run Flask server from. Everything works with a standard flask app. However I am trying to integrate flask-socketio and it keeps failing with different errors depending on how I try to initialize the app.
I have tried the following ways to initialize flask-socketio:
socketio = SocketIO(app.config.from_object(app_config[env_name]))
socketio = SocketIO(app, **app.config[env_name])
socketio = SocketIO(**app.config[env_name])
Here is the relevant code from my app.py file.
def create_app(env_name):
"""
Create app
"""
# app initiliazation
app = Flask(__name__)
app.config.from_object(app_config[env_name])
async_mode = None
# initializing bcrypt and db
bcrypt.init_app(app)
db.init_app(app)
socketio = SocketIO(app.config.from_object(app_config[env_name]))
return socketio
My run.py file looks like this:
rom src.app import create_app
load_dotenv(find_dotenv())
env_name = os.getenv('FLASK_ENV')
app = create_app(env_name)
if __name__ == '__main__':
port = os.getenv('PORT')
# run app
app.run(app, host='0.0.0.0', port=port)
You will notice I am importing from a config.py file. That is where my environment variables are being for (dev, test, prod). Each environment is it's own class. For example:
class Development(object):
"""
Development environment configuration
"""
DEBUG = True
TESTING = False
SQLALCHEMY_TRACK_MODIFICATIONS=False
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
UPLOADED_FILES_DEST = os.getenv('UPLOADED_FILES_DEST')
As you can see, most of those values are set in an environment variable since it is bad practice to put such sensitive information into a repo.
I would like to be able to initialize flask-socketio so I can setup rooms where users can share location based information.
Thanks in advance.
The Flask-SocketIO extensions takes the application instance as an argument. You should configure the application and then initialize it. For example:
app.config.from_object(app_config[env_name])
socketio = SocketIO(app)
I found the issue. I cannot instantiate SocketIO in my app.py file. I have to export app from my app.py file and instantiate SocketIO in my run.py file. My final code looks like this:
app.py
def create_app(env_name):
"""
Create app
"""
# app initiliazation
app = Flask(__name__)
app.config.from_object(app_config[env_name])
# initializing bcrypt and db
bcrypt.init_app(app)
db.init_app(app)
return app
Finally, my run.py file looks like this.
import os
import logging
from dotenv import load_dotenv, find_dotenv
from flask_socketio import SocketIO, join_room, emit
from src.app import create_app
load_dotenv(find_dotenv())
env_name = os.getenv('FLASK_ENV')
app = create_app(env_name)
socketio = SocketIO(app)
if __name__ == '__main__':
port = os.getenv('PORT')
# run app
socketio.run(app, host='0.0.0.0', port=port)
I have an application that works in development, but when I try to run it with Gunicorn it gives an error that the "sqlalchemy extension was not registered". From what I've read it seems that I need to call app.app_context() somewhere, but I'm not sure where. How do I fix this error?
# run in development, works
python server.py
# try to run with gunicorn, fails
gunicorn --bind localhost:8000 server:app
AssertionError: The sqlalchemy extension was not registered to the current application. Please make sure to call init_app() first.
server.py:
from flask.ext.security import Security
from database import db
from application import app
from models import Studio, user_datastore
security = Security(app, user_datastore)
if __name__ == '__main__':
# with app.app_context(): ??
db.init_app(app)
app.run()
application.py:
from flask import Flask
app = Flask(__name__)
app.config.from_object('config.ProductionConfig')
database.py:
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
Only when you start your app with python sever.py is the if __name__ == '__main__': block hit, where you're registering your database with your app.
You'll need to move that line, db.init_app(app), outside that block.
I want to use Werkzeug as a local development server and cannot get the DebugApplication middle ware to work as documented - Werkzeug Debugging. Whats wrong here?
import webapp2
from system import config
from werkzeug.debug import DebuggedApplication
from werkzeug.serving import run_simple
application = webapp2.WSGIApplication(routes=config.routes, debug=False, config=config.options)
debugged_application = DebuggedApplication(application)
def main():
run_simple('localhost', 4000, debugged_application, use_reloader=True, use_debugger=True, threaded=True)
if __name__ == '__main__':
main()
I think that DebuggedApplication middleware tries to achieve the same as use_debugger=True, so no need to use both. The problem is that webapp2.WSGIApplication adds its own error handling before it goes through the debugger middleware, thus forbidding werkzeug debugger to see the actual exception.
My solution to this is to extend base WSGIApplication provided by webapp2 to re-raise the original exception. It works with python 2.7, and will pass the exception if and only if debug flag has been set to True in Application constructor.
class Application(webapp2.WSGIApplication):
def _internal_error(self, exception):
if self.debug:
raise
return super(Application, self)._internal_error(exception)
Not sure this is the cleanest possible way to do it, but it works for me.