-
-
Save astrolox/445e84068d12ed9fa28f277241edf57b to your computer and use it in GitHub Desktop.
""" | |
Flask web application factory. Creates and returns the flask app object. | |
@see http://flask.pocoo.org/docs/1.0/ | |
@author Brian Wojtczak | |
""" | |
import logging | |
import os | |
from flask import Flask | |
from flask_socketio import SocketIO | |
def create_app(test_config: dict = None) -> Flask: | |
# Application instance | |
app = Flask(__name__) | |
# SocketIO extension | |
socketio = SocketIO() | |
socketio.init_app(app) | |
# Functionality via blueprints | |
from . import auth, events | |
app.register_blueprint(auth.bp) | |
app.register_blueprint(events.bp) | |
return app |
""" | |
Dummy Flask Authentication Blueprint. | |
@author Brian Wojtczak | |
""" | |
import functools | |
import logging | |
from flask import Blueprint, request, session, abort | |
logger = logging.getLogger(__name__) | |
bp = Blueprint('auth', __name__) | |
def is_logged_in(): | |
return True | |
def login_required(view): | |
""" | |
View decorator that sends an error to anonymous users. | |
Use the error handler to provide a friendly user experience. | |
""" | |
@functools.wraps(view) | |
def wrapped_view(**kwargs): | |
if not is_logged_in(): | |
abort(401) | |
return view(**kwargs) | |
return wrapped_view | |
@bp.before_app_request | |
def load_logged_in_user(): | |
pass # Not implemented | |
""" | |
Flask Blueprint for handling events on the SocketIO stream. | |
@author Brian Wojtczak | |
""" | |
import functools | |
import logging | |
import time | |
from flask import request | |
from flask_socketio import emit, ConnectionRefusedError, disconnect | |
from .auth import is_logged_in | |
from .io_blueprint import IOBlueprint | |
logger = logging.getLogger(__name__) | |
bp = IOBlueprint('events', __name__) | |
def authenticated_only(f): | |
@functools.wraps(f) | |
def wrapped(*args, **kwargs): | |
if not is_logged_in(): | |
disconnect() | |
else: | |
return f(*args, **kwargs) | |
return wrapped | |
@bp.on('connect') | |
def connect(): | |
if not is_logged_in(): | |
raise ConnectionRefusedError('unauthorized!') | |
emit('flash', 'Welcome ' + request.remote_user) # context aware emit | |
@bp.on('echo') | |
@authenticated_only | |
def on_alive(data): | |
logger.debug(data) | |
emit('echo', data) # context aware emit | |
@bp.on('broadcast') | |
@authenticated_only | |
def on_broadcast(data): | |
logger.debug(data) | |
bp.emit('broadcast', data) # bp.emit same as socketio.emit | |
""" | |
A Flask Blueprint class to be used with Flask-SocketIO. | |
This class inherits from the Flask Blueprint class so that | |
we can use the standard Blueprint interface. | |
Derived from https://github.com/m-housh/io-blueprint | |
Original work by Michael Housh, [email protected] | |
Modified by Brian Wojtczak | |
@author Brian Wojtczak | |
""" | |
# noinspection PyPackageRequirements | |
import socketio | |
from flask import Blueprint | |
class IOBlueprint(Blueprint): | |
def __init__(self, *args, **kwargs): | |
super().__init__(self, *args, **kwargs) | |
self.namespace = self.url_prefix or '/' | |
self._socketio_handlers = [] | |
self.socketio = None | |
self.record_once(self.init_socketio) | |
def init_socketio(self, state): | |
self.socketio: socketio.Client = state.app.extensions['socketio'] | |
for f in self._socketio_handlers: | |
f(self.socketio) | |
return self.socketio | |
def on(self, key): | |
""" A decorator to add a handler to a blueprint. """ | |
def wrapper(f): | |
def wrap(sio): | |
@sio.on(key, namespace=self.namespace) | |
def wrapped(*args, **kwargs): | |
return f(*args, **kwargs) | |
return sio | |
self._socketio_handlers.append(wrap) | |
return wrapper | |
def emit(self, *args, **kwargs): | |
self.socketio.emit(*args, **kwargs) |
Copyright 2020 Brian Wojtczak | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included | |
in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
License?
Added MIT license declaration for you.
I know this is old post, but still. Is this still working? Do you maybe have any idea why this error happens:
if "." in name:
TypeError: argument of type 'IOBlueprint' is not iterable
I get same error with your code and with integrating IOBlueprint into my test project. Thanks in advance.
I used it in a few projects - it works there, but I've not worked on those projects for a while though, so still using older versions of libraries.
eventlet==0.25.1
Flask==1.0.3
Flask-SocketIO==4.1.0
python-socketio==4.2.0
Also please make sure you're using Python 3.
Thank you Brian. It works with those versions. I guess something was changed in some of those modules later I guess. Will deep dive and try to find a fix.
I believe I have found a fix for this. On line 23, delete the first argument to super()
. So:
# go from this
class IOBlueprint(Blueprint):
def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)
# <rest of __init__ is the same>
# to this
class IOBlueprint(Blueprint):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # <= notice the lack of the first `self` argument here
# <rest of __init__ is the same>
Thanks @enjoythecode
@enjoythecode Thanks. I tested it today and I can confirm fix works.
Is there anything else that needs to be taken into consideration if this is using https/nginx/gunicon, still can't seem to catch the emits on the server coming from the client, although server->client works as it should. Great code though, thank you.
Is there anything else that needs to be taken into consideration if this is using https/nginx/gunicon, still can't seem to catch the emits on the server coming from the client, although server->client works as it should. Great code though, thank you.
Just the normal considerations for web sockets.
https://www.nginx.com/blog/websocket-nginx/
License?