class StreamWebsocketServer(Thread, Observer): '''Sends stream changes to web UI.''' def __init__(self, config, store): Thread.__init__(self) self.app = Flask(__name__) self.configure_flask() self.socketio = SocketIO( self.app, logger=logger, engineio_logger=False) self.configure_socketio() self.store = store def on_next(self, stream_change): '''Responds to updates to the stream_changes observable.''' logger.debug('Broadcasting stream change to clients...') logger.debug(str(stream_change)) streams = list(map(lambda stream: stream.data, self.store.read_streams())) streams = sorted( streams, key=lambda stream: stream['viewers'], reverse=True) self.socketio.emit('streams', streams, broadcast=True) def configure_flask(self): self.app.config['SECRET_KEY'] = 'secret!' def handler(): return render_template('eternal-twitch.html') self.app.add_url_rule('/', 'eternal_twitch', handler) def configure_socketio(self): def handle_connection(): self.on_next({ 'type': 'connection' }) self.socketio.on_event('connected', handle_connection) def run(self): '''Subscribes websocket server to changes to the store.''' self.disposer = self.store.stream_changes.pipe( debounce(1) ).subscribe(self) self.socketio.run(self.app, host='0.0.0.0', debug=False, use_reloader=False) def stop_socketio(self): def handler(): self.socketio.stop() self.socketio.on('stop', handler) def stop(self): '''Stops sending store updates to websocket server.''' logger.info('Stopping stream websocket server...') if hasattr(self, 'disposer'): self.disposer.dispose() self.stop_socketio()
def test_recieve_msg(self): global msg def on_aaa_response(*args): global msg msg = args[0] def on_connect(): print('connect') socketIO = SocketIO('localhost', 5000) socketIO.on('connect', on_connect) socketIO.on('my response', on_aaa_response) socketIO.emit('my event', {'message': 'test', "user_name": 'test'}) socketIO.wait(seconds=1) self.assertEqual(msg, {u'message': u'test', u'user_name': u'test'})
def test_delete_client(self): global msg def on_aaa_response(*args): global msg msg = args[0] def on_connect(): print('connect') socketIO = SocketIO('localhost', 5000) socketIO.on('connect', on_connect) socketIO.on('my response', on_aaa_response) socketIO.emit('my event', {'data': u'Anonimus Disconnected'}) socketIO.wait(seconds=1) self.assertEqual(msg, {u'message': u'Anonimus left chat room', u'user_name': u'server'})
def test_new_client(self): global msg def on_aaa_response(*args): global msg msg = args[0] def on_connect(): print('connect') socketIO = SocketIO('localhost', 5000) socketIO.on('connect', on_connect) socketIO.on('my response', on_aaa_response) socketIO.emit('my event', {u'data': u'User Connected'}) socketIO.wait(seconds=1) self.assertEqual(msg, {u'message': u'One more user!', u'user_name': u'server'})
class SocketIOManager(BaseManager): def __init__(self, appbuilder): super(SocketIOManager, self).__init__(appbuilder) app = appbuilder.get_app self.socketio = SocketIO(app) def run(self, *args, **kwargs): return self.socketio.run(*args, **kwargs) def on(self, *args, **kwargs): return self.socketio.on(*args, **kwargs)
class Waffuru: def __init__(self, mode="chrome", host="localhost", port=8000, size=WindowSize(800, 600), position=WindowPosition(0, 0), close_callback=None, app_mode= True): self.mode = mode self.host = host self.port = port self.size = size self.position = position self.close_callback = close_callback self.app_mode = app_mode self._exposed_functions = {} self.app = Flask("Waffuru") self.app.config['SECRET_KEY'] = 'secret!' self.socketio = SocketIO(self.app) self.app.route("/")(self.index) self.socketio.on("my event")(self.test_message) def expose(self, name, func): if name in self._exposed_functions: logger.warning("{} is existing".format(name)) self._exposed_functions[name] = func def get_agent(self): pass def index(self): return "Hello World" def test_message(self, message): emit('my response', {'data': 'got it!'})
result = db.execute( 'SELECT * FROM flaskUsers WHERE email = :email AND password = :password', { "email": email, "password": password }).fetchone() if result: session['name'] = result.name session['email'] = result.email return jsonify({"login": True, "result": [row for row in result]}) return jsonify({ "login": False, "error": "User entered a wrong email or password" }) @app.route('/logout', methods=["POST"]) def logout(): session.pop('name') session.pop('email') return jsonify({"logout": True}) socketio.on('send message') def send_message(data): message = data['message'] emit('recieve message', {'message': message}, broadcast=True)
from flask_socketio import SocketIO #sio = SocketIO(message_queue="redis://") sio = SocketIO(message_queue="amqp://") from socketIO_client import SocketIO def on_chat_response(*args): print 'on_chat_response', args print "Getting sio" socketIO = SocketIO('localhost', 80) print "Got sio", socketIO socketIO.on('chat', on_chat_response) socketIO.emit('chat', "hello") socketIO.wait(seconds=3)
class will: @atexit.register def dump_events(self, *args): ''' Dump events to db on exit :return: ''' try: log.info(":SYS:Dumping events") except: print(":SYS: Error with logging while dumping events") for event in core.events: if event["type"] != "function": try: log.debug(":SYS:Dumping event {0}".format(event)) db["events"].upsert(event, ['uid']) except: print(":SYS:Error encountered while dumping events") def start(self): """ Initialize db and session monitor thread """ global db log.info(":SYS:Starting W.I.L.L") db_url = self.configuration_data["db_url"] log.info(":SYS:Connecting to database") db = dataset.connect(db_url, engine_kwargs={"pool_recycle": 1}) core.db = db API.db = db web.db = db start_time = self.now.strftime("%I:%M %p %A %m/%d/%Y") web.start_time = start_time log.info(":SYS:Starting W.I.L.L core") core.initialize(db) log.info(":SYS:Starting sessions parsing thread") core.sessions_monitor(db) log.info(":SYS:W.I.L.L started") def __init__(self): self.now = datetime.datetime.now() conf_file = "will.conf" if os.path.isfile("debug_will.conf"): conf_file = "debug_will.conf" self.conf_file = conf_file if os.path.isfile(conf_file): data_string = open(conf_file).read() json_data = json.loads(data_string) self.configuration_data = json_data API.configuration_data = self.configuration_data web.configuration_data = self.configuration_data else: print("Couldn't find will.conf file, exiting") os._exit(1) self.db = None app = Flask(__name__) app.register_blueprint(web.web) app.register_blueprint(API.api, url_prefix="/api") self.app = app app.logger.setLevel(logging.DEBUG) app.logger.addHandler(logging.StreamHandler(sys.stdout)) app.secret_key = self.configuration_data["secret_key"] logfile = self.configuration_data["logfile"] logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', filemode='w', filename=logfile) ch = logging.StreamHandler(sys.stdout) if self.configuration_data["debug"]: ch.setLevel(logging.DEBUG) else: ch.setLevel(logging.INFO) handler = RotatingFileHandler(logfile, maxBytes=10000000, backupCount=5) handler.setLevel(logging.DEBUG) self.app.logger.addHandler(handler) global log log = self.app.logger self.socketio = SocketIO(app) web.socketio = self.socketio self.socketio.on(web.disconnect_session, 'disconnect') self.socketio.on(web.get_updates, "get_updates") signal.signal(signal.SIGTERM, self.dump_events) self.start() self.socketio.run(app, host=self.configuration_data["host"], port=self.configuration_data["port"], debug=self.configuration_data["debug"], use_reloader=False)
class CarpentryHttpServer(Web): def __init__(self, log_level=logger.info, *args, **kw): super(CarpentryHttpServer, self).__init__(*args, **kw) setup_logging(log_level) self.flask_app.config.from_object('carpentry.conf') self.github = GitHub(self.flask_app) self.prepare_services_integration() def prepare_services_integration(self): self.setup_github_authentication() MODULES.clear() # self.collect_websocket_modules() self.collect_modules() def collect_websocket_modules(self): self.socket_io = SocketIO(self.flask_app) self.websockets = [] for event, handler in WEBSOCKET_HANDLERS.items(): register = self.socket_io.on(event) self.websockets.append(register(handler)) return self.websockets def setup_github_authentication(self): @self.flask_app.before_request def prepare_user(): carpentry_token = request.cookies.get('carpentry_token') g.user = User.from_carpentry_token(carpentry_token) @self.github.access_token_getter def token_getter(): user = g.user if user is not None: return user.github_access_token @self.flask_app.route('/oauth/github.com') @self.github.authorized_handler def authorized(access_token): next_url = request.args.get('next') or conf.get_full_url('/') access_token = access_token or request.args.get('access_token') if access_token is None: logging.warning( "No access token retrieved, set the log level " "to debug and check flask-github's output. " "You likely set the wrong github client id " "and secret", access_token) return redirect(next_url) users = User.objects.filter(github_access_token=access_token) user_exists = len(users) > 0 if not user_exists: g.user = User( id=uuid.uuid1(), carpentry_token=uuid.uuid4(), github_access_token=access_token ) logger.info("created new user %s", g.user) else: logger.info( "User already exists with github_access_token %s %s", access_token, g.user) g.user = users[0] g.user.carpentry_token = uuid.uuid4() g.user.github_access_token = access_token g.user.save() logging.warning("authorized: %s - token: %s", g.user, access_token) response = redirect(next_url) response.set_cookie( 'carpentry_token', bytes(g.user.carpentry_token), ) return response @self.flask_app.route('/login', methods=["GET"]) def login(): response = self.github.authorize( scope='repo_deployment,repo,user,gist,write:repo_hook,repo:status,org:admin,admin:org_hook') return response @self.flask_app.route('/logout', methods=["GET"]) def logout(): response = redirect('/') if g.user: g.user.reset_token() response.set_cookie('carpentry_token', '', expires=0) return response
class App: """Core class to layout, connect, build a Bowtie app.""" def __init__(self, name='__main__', app=None, rows: int = 1, columns: int = 1, sidebar: bool = False, title: str = 'Bowtie App', theme: Optional[str] = None, background_color: str = 'White', socketio: str = '', debug: bool = False) -> None: """Create a Bowtie App. Parameters ---------- name : str, optional Use __name__ or leave as default if using a single module. Consult the Flask docs on "import_name" for details on more complex apps. app : Flask app, optional If you are defining your own Flask app, pass it in here. You only need this if you are doing other stuff with Flask outside of bowtie. row : int, optional Number of rows in the grid. columns : int, optional Number of columns in the grid. sidebar : bool, optional Enable a sidebar for control components. title : str, optional Title of the HTML. theme : str, optional Color for Ant Design components. background_color : str, optional Background color of the control pane. socketio : string, optional Socket.io path prefix, only change this for advanced deployments. debug : bool, optional Enable debugging in Flask. Disable in production! """ self.title = title self.theme = theme self._init: Optional[Callable] = None self._socketio_path = socketio self._schedules: List[Scheduler] = [] self._subscriptions: Dict[Event, List[Tuple[List[Event], Callable]]] = defaultdict(list) self._pages: Dict[Pager, Callable] = {} self._uploads: Dict[int, Callable] = {} self._root = View(rows=rows, columns=columns, sidebar=sidebar, background_color=background_color) self._routes: List[Route] = [] self._package_dir = Path(os.path.dirname(__file__)) self._jinjaenv = Environment( loader=FileSystemLoader(str(self._package_dir / 'templates')), trim_blocks=True, lstrip_blocks=True ) if app is None: self.app = Flask(name) else: self.app = app self.app.debug = debug self._socketio = SocketIO(self.app, binary=True, path=socketio + 'socket.io') self.app.secret_key = secrets.token_bytes() self.add_route(view=self._root, path='/', exact=True) # https://buxty.com/b/2012/05/custom-template-folders-with-flask/ templates = Path(__file__).parent / 'templates' self.app.jinja_loader = ChoiceLoader([ # type: ignore self.app.jinja_loader, FileSystemLoader(str(templates)), ]) self._build_dir = self.app.root_path / _DIRECTORY # type: ignore self.app.before_first_request(self._endpoints) def wsgi_app(self, environ, start_response): """Support uwsgi and gunicorn.""" return self.app.wsgi_app(environ, start_response) def __call__(self, environ, start_response): """Support uwsgi and gunicorn.""" return self.wsgi_app(environ, start_response) def __getattr__(self, name: str): """Export attributes from root view.""" if name == 'columns': return self._root.columns if name == 'rows': return self._root.rows if name == 'column_gap': return self._root.column_gap if name == 'row_gap': return self._root.row_gap if name == 'border': return self._root.border if name == 'layout': return self._root.layout raise AttributeError(name) def __setattr__(self, name, value): """Set layout function for root view.""" if name == 'layout': return self._root.__setattr__(name, value) return super().__setattr__(name, value) def __getitem__(self, key: Any): """Get item from root view.""" return self._root.__getitem__(key) def __setitem__(self, key: Any, value: Union[Component, Sequence[Component]]) -> None: """Add widget to the root view.""" self._root.__setitem__(key, value) def add(self, component: Component) -> None: """Add a widget to the grid in the next available cell. Searches over columns then rows for available cells. Parameters ---------- component : bowtie._Component A Bowtie component instance. """ self._root.add(component) def add_sidebar(self, widget: Component) -> None: """Add a widget to the sidebar. Parameters ---------- widget : bowtie._Component Add this widget to the sidebar, it will be appended to the end. """ self._root.add_sidebar(widget) def add_route(self, view: View, path: str, exact: bool = True) -> None: """Add a view to the app. Parameters ---------- view : View path : str exact : bool, optional """ if path[0] != '/': path = '/' + path for route in self._routes: assert path != route.path, 'Cannot use the same path twice' self._routes.append(Route(view=view, path=path, exact=exact)) self.app.add_url_rule( path, path[1:], lambda: render_template('bowtie.html', title=self.title) ) def subscribe(self, *events: Union[Event, Pager]) -> Callable: """Call a function in response to an event. If more than one event is given, `func` will be given as many arguments as there are events. If the pager calls notify, the decorated function will be called. Parameters ---------- *event : event or pager Bowtie event, must have at least one. Examples -------- Subscribing a function to multiple events. >>> from bowtie.control import Dropdown, Slider >>> app = App() >>> dd = Dropdown() >>> slide = Slider() >>> @app.subscribe(dd.on_change, slide.on_change) ... def callback(dd_item, slide_value): ... pass >>> @app.subscribe(dd.on_change) ... @app.subscribe(slide.on_change) ... def callback2(value): ... pass Using the pager to run a callback function. >>> from bowtie.pager import Pager >>> app = App() >>> pager = Pager() >>> @app.subscribe(pager) ... def callback(): ... pass >>> def scheduledtask(): ... pager.notify() """ try: first_event = events[0] except IndexError: raise IndexError('Must subscribe to at least one event.') if len(events) != len(set(events)): raise ValueError( 'Subscribed to the same event multiple times. All events must be unique.' ) if len(events) > 1: # check if we are using any non stateful events for event in events: if isinstance(event, Pager): raise NotStatefulEvent('Pagers must be subscribed by itself.') if event.getter is None: raise NotStatefulEvent( f'{event.uuid}.on_{event.name} is not a stateful event. ' 'It must be used alone.' ) def decorator(func: Callable) -> Callable: """Handle three types of events: pages, uploads, and normal events.""" if isinstance(first_event, Pager): self._pages[first_event] = func elif first_event.name == 'upload': if first_event.uuid in self._uploads: warnings.warn( ('Overwriting function "{func1}" with function ' '"{func2}" for upload object "{obj}".').format( func1=self._uploads[first_event.uuid], func2=func.__name__, obj=COMPONENT_REGISTRY[first_event.uuid] ), Warning) self._uploads[first_event.uuid] = func else: for event in events: # need to have `events` here to maintain order of arguments # not sure how to deal with mypy typing errors on events so ignoring self._subscriptions[event].append((events, func)) # type: ignore return func return decorator def load(self, func: Callable) -> Callable: """Call a function on page load. Parameters ---------- func : callable Function to be called. """ self._init = func return func def schedule(self, seconds: float): """Call a function periodically. Parameters ---------- seconds : float Minimum interval of function calls. func : callable Function to be called. """ def wrap(func: Callable): self._schedules.append(Scheduler(self.app, seconds, func)) return wrap def _write_templates(self) -> Set[str]: indexjsx = self._jinjaenv.get_template('index.jsx.j2') componentsjs = self._jinjaenv.get_template('components.js.j2') webpack = self._jinjaenv.get_template('webpack.common.js.j2') src = self._create_jspath() webpack_path = self._build_dir / webpack.name[:-3] # type: ignore with webpack_path.open('w') as f: f.write( webpack.render(color=self.theme) ) # copy js modules that are always needed for name in ['progress.jsx', 'view.jsx', 'utils.js']: template_src = self._package_dir / 'src' / name shutil.copy(template_src, src) # Layout Design # # Dictionaries that are keyed by the components # # To layout this will need to look through all components that have a key of the route # # use cases # 1. statically add items to controller in list # 2. remove item from controller # 3. add item back to controller # # issues: # widget reordering # order preserving operations components: Set[Component] = set() imports: Set[_Import] = set() packages: Set[str] = set() for route in self._routes: if route.view.layout: route.view.layout() packages |= route.view._packages # pylint: disable=protected-access imports |= route.view._imports # pylint: disable=protected-access components |= route.view._components # pylint: disable=protected-access for template in route.view._templates: # pylint: disable=protected-access template_src = self._package_dir / 'src' / template shutil.copy(template_src, src) with (src / componentsjs.name[:-3]).open('w') as f: # type: ignore f.write( componentsjs.render( imports=imports, socketio=self._socketio_path, components=components, ) ) with (src / indexjsx.name[:-3]).open('w') as f: # type: ignore f.write( indexjsx.render( maxviewid=View._NEXT_UUID, # pylint: disable=protected-access socketio=self._socketio_path, pages=self._pages, routes=self._routes, ) ) return packages def _build(self, notebook: Optional[str] = None) -> None: """Compile the Bowtie application.""" if node_version() < _MIN_NODE_VERSION: raise WebpackError( f'Webpack requires at least version {_MIN_NODE_VERSION} of Node, ' f'found version {node_version}.' ) packages = self._write_templates() for filename in ['package.json', 'webpack.prod.js', 'webpack.dev.js']: if not (self._build_dir / filename).is_file(): sourcefile = self._package_dir / 'src' / filename shutil.copy(sourcefile, self._build_dir) if self._run(['yarn', '--ignore-engines', 'install'], notebook=notebook) > 1: raise YarnError('Error installing node packages') if packages: installed = self._installed_packages() new_packages = [x for x in packages if x.split('@')[0] not in installed] if new_packages: retval = self._run( ['yarn', '--ignore-engines', 'add'] + new_packages, notebook=notebook ) if retval == 1: print('Yarn error but trying to continue build') elif retval > 1: raise YarnError('Error installing node packages') retval = self._run([_WEBPACK, '--config', 'webpack.dev.js'], notebook=notebook) if retval != 0: raise WebpackError('Error building with webpack') def _endpoints(self): def generate_sio_handler(main_event, supports): # get all events from all subscriptions associated with this event uniq_events = set() for events, _ in supports: uniq_events.update(events) uniq_events.remove(main_event) for event in uniq_events: comp = COMPONENT_REGISTRY[event.uuid] if event.getter is None: raise AttributeError( f'{comp} has no getter associated with event "on_{event.name}"' ) def handler(*args): def wrapuser(): event_data = {} for event in uniq_events: comp = COMPONENT_REGISTRY[event.uuid] # we already checked that this component has a getter event_data[event.signal] = getattr(comp, event.getter)() # if there is no getter, then there is no data to unpack # if there is a getter, then we need to unpack the data sent main_getter = main_event.getter if main_getter is not None: comp = COMPONENT_REGISTRY[main_event.uuid] event_data[main_event.signal] = getattr(comp, '_' + main_getter)( msgpack.unpackb(args[0], encoding='utf8') ) # gather the remaining data from the other events through their getter methods for events, func in supports: if main_getter is not None: func(*(event_data[event.signal] for event in events)) else: func() # TODO replace with flask socketio start_background_task eventlet.spawn(copy_current_request_context(wrapuser)) return handler for event, supports in self._subscriptions.items(): self._socketio.on(event.signal)(generate_sio_handler(event, supports)) if self._init is not None: self._socketio.on('INITIALIZE')(lambda: eventlet.spawn( copy_current_request_context(self._init) )) def gen_upload(func): def upload(): upfile = request.files['file'] retval = func(upfile.filename, upfile.stream) if retval: return make_response(jsonify(), 400) return make_response(jsonify(), 200) return upload for uuid, func in self._uploads.items(): self.app.add_url_rule( f'/upload{uuid}', f'upload{uuid}', gen_upload(func), methods=['POST'] ) for page, func in self._pages.items(): # pylint: disable=protected-access self._socketio.on(f'resp#{page._uuid}')(lambda: eventlet.spawn( copy_current_request_context(func) )) # bundle route @self.app.route('/bowtie/bundle.js') def bowtiebundlejs(): # pylint: disable=unused-variable bundle_path = self.app.root_path + '/build/bundle.js' bundle_path_gz = bundle_path + '.gz' try: if os.path.getmtime(bundle_path) > os.path.getmtime(bundle_path_gz): return open(bundle_path, 'r').read() bundle = open(bundle_path_gz, 'rb').read() response = flask.make_response(bundle) response.headers['content-encoding'] = 'gzip' response.headers['vary'] = 'accept-encoding' response.headers['content-length'] = len(response.data) return response except FileNotFoundError: if os.path.isfile(bundle_path_gz): bundle = open(bundle_path_gz, 'rb').read() response = flask.make_response(bundle) response.headers['Content-Encoding'] = 'gzip' response.headers['Vary'] = 'Accept-Encoding' response.headers['Content-Length'] = len(response.data) return response return open(bundle_path, 'r').read() for schedule in self._schedules: schedule.start() def _serve(self, host='0.0.0.0', port=9991) -> None: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((host, port)) if result == 0: raise Exception(f'Port {port} is unavailable on host {host}, aborting.') self._socketio.run(self.app, host=host, port=port) for schedule in self._schedules: schedule.stop() def _installed_packages(self) -> Generator[str, None, None]: """Extract installed packages as list from `package.json`.""" with (self._build_dir / 'package.json').open('r') as f: packages = json.load(f) yield from packages['dependencies'].keys() def _create_jspath(self) -> Path: """Create the source directory for the build.""" src = self._build_dir / 'bowtiejs' os.makedirs(src, exist_ok=True) return src def _run(self, command: List[str], notebook: Optional[str] = None) -> int: """Run command from terminal and notebook and view output from subprocess.""" if notebook is None: return Popen(command, cwd=self._build_dir).wait() cmd = Popen(command, cwd=self._build_dir, stdout=PIPE, stderr=STDOUT) while True: line = cmd.stdout.readline() if line == b'' and cmd.poll() is not None: return cmd.poll() print(line.decode('utf-8'), end='') raise Exception()
ch.setLevel(logging.INFO) handler = RotatingFileHandler(logfile, maxBytes=10000000, backupCount=5) handler.setLevel(logging.DEBUG) app.logger.addHandler(handler) log = app.logger socketio = SocketIO(app) # Internal imports import core import API import web web.socketio = socketio socketio.on(web.disconnect_session, 'disconnect') socketio.on(web.get_updates, "get_updates") @atexit.register def dump_events(*args): ''' Dump events to db on exit :return: ''' try: log.info(":SYS:Dumping events") except: print(":SYS: Error with logging while dumping events") for event in core.events: if event["type"] != "function":
import sockets DOWNLOAD_DIR = './downloads/' app = Flask(__name__) socketio = SocketIO(app, async_mode='gevent', ping_timeout=30) @app.route('/') def index(): return send_from_directory('static', 'index.html') @app.route('/static/<path:path>') def send_static(path): return send_from_directory('static', path) @app.route('/downloads/<path:path>') def send_downloaded(path): return send_from_directory(DOWNLOAD_DIR, path) if __name__ == "__main__": gevent.monkey.patch_all() socketio.on('connect')(sockets.create_user) socketio.on('disconnect')(sockets.destroy_user) socketio.on('parse')(sockets.parse_url) socketio.on('start_dl')(sockets.start_dl) socketio.run(app, '127.0.0.1', 8001)
return jsonify({'status':'ok'}) # socketio.on('connect') # def login(socket): # gl.online_num += 1 # print('Num:' + gl.online_num) # socketio.on('disconnect') # def logout(socket): # gl.online_num -= 1 # # socketio.on('user') # def user(): # print('Num:' + gl.online_num) # send(gl.online_num, broadcast=True) socketio.on('message') def handleMessage(msg): print('Message:'+msg) send(msg, broadcast=True) if __name__ == '__main__': socketio.run(app, host='127.0.0.1', port=9000, debug=True) #app.run(debug=True)
import eventlet eventlet.monkey_patch() # for flask_socketio message queue support from app import create_app from app.routes import routes, handlers from flask_socketio import SocketIO app = create_app(blueprints=[routes]) socketio = SocketIO(app, message_queue='redis://localhost:6379') # have to do this manually for msg, funcs in handlers.items(): for ns, func in funcs.items(): socketio.on(msg, namespace=ns)(func) socketio.run(app, host='0.0.0.0', port=5000, debug=True)
class ServerFlaskCommunicator(ServerCommunicator): def __init__(self) -> None: super().__init__() static_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'role') self._app = Flask(__name__, template_folder=os.path.join(static_path, 'templates'), static_folder=os.path.join(static_path, 'static')) self._app.config['SECRET_KEY'] = ConfigurationManager( ).runtime_config.secret_key self._socketio = ServerSocketIO(self._app, max_http_buffer_size=1e20, async_handlers=True, ping_timeout=3600, ping_interval=1800, cors_allowed_origins='*') logging.getLogger('werkzeug').setLevel(logging.ERROR) def on(self, event: ClientEvent) -> Callable[[Callable], Any]: return self._socketio.on(event2message(event)) def route(self, rule: str, **options: Any): return self._app.route(rule, **options) def invoke(self, event: ClientEvent, *args, callee: Optional[ClientId] = None, **kwargs): room_id = request.sid if callee is not None: with self._client_node_ctx_mgr.get_by_client(callee) as c_node_ctx: room_id = c_node_ctx.comm_id return emit(event2message(event), *args, room=room_id, **kwargs) def invoke_all(self, event: ClientEvent, payload: Optional[Dict[str, Any]] = None, *args, callees: Optional[Iterable[ClientId]] = None, **kwargs): msg = event2message(event) if callees is None: if payload is None: return emit(msg, *args, **kwargs, broadcast=True) else: return emit(msg, payload, *args, **kwargs, broadcast=True) else: res = list() for node_id, client_ids in self._client_node_ctx_mgr.cluster_by_node( callees).items(): if payload is None: payload = dict() payload['selected_clients'] = list(client_ids) with self._client_node_ctx_mgr.get_by_node( node_id) as c_node_ctx: room_id = c_node_ctx.comm_id res.append( emit(msg, payload, *args, room=room_id, **kwargs)) return res def run_server(self) -> None: self._socketio.run(self._app, host=self._host, port=self._port) def handle_disconnection(self) -> Iterable[ClientId]: return self._handle_disconnection(request.sid) def handle_reconnection(self) -> Iterable[ClientId]: return self._handle_reconnection(request.sid) def activate(self, node_id: NodeId, client_ids: Iterable[ClientId]) -> None: self._activate(node_id, request.sid, client_ids)
status = "successful" # print(token) res_data['token'] = token res_data['user'] = user else: message = "wrong password" code = 401 status = "fail" else: message = "invalid login details" code = 401 status = "fail" except Exception as ex: message = f"{ex}" code = 500 status = "fail" return jsonify({'status': status, "data": res_data, "message":message}), code class MyCustomNamespace(Namespace): def on_connect(self): print("Client just connected") def on_disconnect(self): print("Client just left") socketio.on(MyCustomNamespace('/socket')) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port='8000')
"""The socketio package.""" from flask_socketio import SocketIO, emit from socket_server.handler.client_connect import client_connect from socket_server.handler.client_disconnect import client_disconnect socket_api = SocketIO() socket_api.on('connect', client_connect) socket_api.on('disconnect', client_disconnect)
@app.route("/login", methods=['GET', 'POST']) def login(): #if request.method == 'POST': # return redirect("/chat") return render_template("login.html") @app.route("/chat", methods=['POST']) def chat(): return render_template('chat.html', rooms=ROOMS, four=hundred_messages[-3:]) socketio.on('deleteMessages') def delete_messages(data): name = data['username'] #delete messages from server by sender pass @socketio.on('message') def message(data): msg = data['msg'] username = data['username'] room = data['room'] time_stamp = strftime('%b-%d %I:%M%p', localtime())
from web_controller import WebController from flask import Flask from flask_socketio import SocketIO import RPi.GPIO as GPIO app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socket_io = SocketIO(app) #socket_io = SocketIO(app, logger=True, engineio_logger=True) web_controller = WebController() app.route('/')(web_controller.index) socket_io.on('scan_storage')(web_controller.scan_storage) socket_io.on('backward_car')(web_controller.backward_car) socket_io.on('stop_car')(web_controller.stop_car) if __name__ == "__main__": try: socket_io.run(app, host='0.0.0.0') finally: GPIO.cleanup()
def handle_message(message): print('received message: ' + message) output_img = cv2.imread(input_img_path) def on_connect(): print('connect') def on_disconnect(): print('disconnect') def on_reconnect(): print('reconnect') socketIO = SocketIO('192.168.1.104', 3334, LoggingNamespace) socketIO.on('connection', on_connect) socketIO.on('disconnect', on_disconnect) socketIO.on('reconnect', on_reconnect) retrval, output_img = cv2.imencode('.jpg', output_img) output_img = base64.b64encode(output_img) output_img = str(output_img) socketIO.emit('x', output_img) socketIO.wait(50)
raise RuntimeError() @socketio.on_error_default def default_error_handler(e): print(e) emit('error', 'Something went wrong') print(request.event["message"]) print(request.event["args"]) raise e event_map = { 'build_pattern': build_pattern, 'refine_pattern': refine_pattern, 'find_matches': find_matches, 'find_matches_all_patterns': find_matches_all_patterns, 'delete_all_pattern_matches': delete_all_pattern_matches, 'refresh_pattern_matches': refresh_pattern_matches, 'visualise_sentence': visualise_sentence, 'visualise_pattern': visualise_pattern, 'visualise_match': visualise_match, } for event, func in event_map.items(): decorator = socketio.on(event) decorator(func) if __name__ == '__main__': socketio.run(app, debug=True, port=config['port'])
class App: """Core class to layout, connect, build a Bowtie app.""" def __init__(self, name='__main__', app=None, rows: int = 1, columns: int = 1, sidebar: bool = False, title: str = 'Bowtie App', theme: Optional[str] = None, background_color: str = 'White', socketio: str = '', debug: bool = False) -> None: """Create a Bowtie App. Parameters ---------- name : str, optional Use __name__ or leave as default if using a single module. Consult the Flask docs on "import_name" for details on more complex apps. app : Flask app, optional If you are defining your own Flask app, pass it in here. You only need this if you are doing other stuff with Flask outside of bowtie. row : int, optional Number of rows in the grid. columns : int, optional Number of columns in the grid. sidebar : bool, optional Enable a sidebar for control components. title : str, optional Title of the HTML. theme : str, optional Color for Ant Design components. background_color : str, optional Background color of the control pane. socketio : string, optional Socket.io path prefix, only change this for advanced deployments. debug : bool, optional Enable debugging in Flask. Disable in production! """ self.title = title self.theme = theme self._init: Optional[Callable] = None self._socketio_path = socketio self._schedules: List[Scheduler] = [] self._subscriptions: Dict[Event, List[Tuple[List[Event], Callable]]] = defaultdict(list) self._pages: Dict[Pager, Callable] = {} self._uploads: Dict[int, Callable] = {} self._root = View(rows=rows, columns=columns, sidebar=sidebar, background_color=background_color) self._routes: List[Route] = [] self._package_dir = Path(os.path.dirname(__file__)) self._jinjaenv = Environment( loader=FileSystemLoader(str(self._package_dir / 'templates')), trim_blocks=True, lstrip_blocks=True ) if app is None: self.app = Flask(name) else: self.app = app self.app.debug = debug self._socketio = SocketIO(self.app, binary=True, path=socketio + 'socket.io') self.app.secret_key = secrets.token_bytes() self.add_route(view=self._root, path='/', exact=True) # https://buxty.com/b/2012/05/custom-template-folders-with-flask/ templates = Path(__file__).parent / 'templates' self.app.jinja_loader = ChoiceLoader([ self.app.jinja_loader, FileSystemLoader(str(templates)), ]) self._build_dir = self.app.root_path / _DIRECTORY self.app.before_first_request(self._endpoints) def wsgi_app(self, environ, start_response): """Support uwsgi and gunicorn.""" return self.app.wsgi_app(environ, start_response) def __call__(self, environ, start_response): """Support uwsgi and gunicorn.""" return self.wsgi_app(environ, start_response) def __getattr__(self, name: str): """Export attributes from root view.""" if name == 'columns': return self._root.columns if name == 'rows': return self._root.rows if name == 'column_gap': return self._root.column_gap if name == 'row_gap': return self._root.row_gap if name == 'border': return self._root.border if name == 'layout': return self._root.layout raise AttributeError(name) def __setattr__(self, name, value): """Set layout function for root view.""" if name == 'layout': return self._root.__setattr__(name, value) return super().__setattr__(name, value) def __getitem__(self, key: Any): """Get item from root view.""" return self._root.__getitem__(key) def __setitem__(self, key: Any, value: Union[Component, Sequence[Component]]) -> None: """Add widget to the root view.""" self._root.__setitem__(key, value) def add(self, component: Component) -> None: """Add a widget to the grid in the next available cell. Searches over columns then rows for available cells. Parameters ---------- component : bowtie._Component A Bowtie component instance. """ self._root.add(component) def add_sidebar(self, widget: Component) -> None: """Add a widget to the sidebar. Parameters ---------- widget : bowtie._Component Add this widget to the sidebar, it will be appended to the end. """ self._root.add_sidebar(widget) def add_route(self, view: View, path: str, exact: bool = True) -> None: """Add a view to the app. Parameters ---------- view : View path : str exact : bool, optional """ if path[0] != '/': path = '/' + path for route in self._routes: assert path != route.path, 'Cannot use the same path twice' self._routes.append(Route(view=view, path=path, exact=exact)) self.app.add_url_rule( path, path[1:], lambda: render_template('bowtie.html', title=self.title) ) def subscribe(self, *events: Union[Event, Pager]) -> Callable: """Call a function in response to an event. If more than one event is given, `func` will be given as many arguments as there are events. If the pager calls notify, the decorated function will be called. Parameters ---------- *event : event or pager Bowtie event, must have at least one. Examples -------- Subscribing a function to multiple events. >>> from bowtie.control import Dropdown, Slider >>> app = App() >>> dd = Dropdown() >>> slide = Slider() >>> @app.subscribe(dd.on_change, slide.on_change) ... def callback(dd_item, slide_value): ... pass >>> @app.subscribe(dd.on_change) ... @app.subscribe(slide.on_change) ... def callback2(value): ... pass Using the pager to run a callback function. >>> from bowtie.pager import Pager >>> app = App() >>> pager = Pager() >>> @app.subscribe(pager) ... def callback(): ... pass >>> def scheduledtask(): ... pager.notify() """ try: first_event = events[0] except IndexError: raise IndexError('Must subscribe to at least one event.') if len(events) != len(set(events)): raise ValueError( 'Subscribed to the same event multiple times. All events must be unique.' ) if len(events) > 1: # check if we are using any non stateful events for event in events: if isinstance(event, Pager): raise NotStatefulEvent('Pagers must be subscribed by itself.') if event.getter is None: raise NotStatefulEvent( f'{event.uuid}.on_{event.name} is not a stateful event. ' 'It must be used alone.' ) def decorator(func: Callable) -> Callable: """Handle three types of events: pages, uploads, and normal events.""" if isinstance(first_event, Pager): self._pages[first_event] = func elif first_event.name == 'upload': if first_event.uuid in self._uploads: warnings.warn( ('Overwriting function "{func1}" with function ' '"{func2}" for upload object "{obj}".').format( func1=self._uploads[first_event.uuid], func2=func.__name__, obj=COMPONENT_REGISTRY[first_event.uuid] ), Warning) self._uploads[first_event.uuid] = func else: for event in events: # need to have `events` here to maintain order of arguments # not sure how to deal with mypy typing errors on events so ignoring self._subscriptions[event].append((events, func)) # type: ignore return func return decorator def load(self, func: Callable) -> Callable: """Call a function on page load. Parameters ---------- func : callable Function to be called. """ self._init = func return func def schedule(self, seconds: float): """Call a function periodically. Parameters ---------- seconds : float Minimum interval of function calls. func : callable Function to be called. """ def wrap(func: Callable): self._schedules.append(Scheduler(self.app, seconds, func)) return wrap def _write_templates(self) -> Set[str]: indexjsx = self._jinjaenv.get_template('index.jsx.j2') componentsjs = self._jinjaenv.get_template('components.js.j2') webpack = self._jinjaenv.get_template('webpack.common.js.j2') src = self._create_jspath() webpack_path = self._build_dir / webpack.name[:-3] # type: ignore with webpack_path.open('w') as f: f.write( webpack.render(color=self.theme) ) # copy js modules that are always needed for name in ['progress.jsx', 'view.jsx', 'utils.js']: template_src = self._package_dir / 'src' / name shutil.copy(template_src, src) # Layout Design # # Dictionaries that are keyed by the components # # To layout this will need to look through all components that have a key of the route # # use cases # 1. statically add items to controller in list # 2. remove item from controller # 3. add item back to controller # # issues: # widget reordering # order preserving operations components: Set[Component] = set() imports: Set[_Import] = set() packages: Set[str] = set() for route in self._routes: if route.view.layout: route.view.layout() packages |= route.view._packages # pylint: disable=protected-access imports |= route.view._imports # pylint: disable=protected-access components |= route.view._components # pylint: disable=protected-access for template in route.view._templates: # pylint: disable=protected-access template_src = self._package_dir / 'src' / template shutil.copy(template_src, src) with (src / componentsjs.name[:-3]).open('w') as f: # type: ignore f.write( componentsjs.render( imports=imports, socketio=self._socketio_path, components=components, ) ) with (src / indexjsx.name[:-3]).open('w') as f: # type: ignore f.write( indexjsx.render( maxviewid=View._NEXT_UUID, # pylint: disable=protected-access socketio=self._socketio_path, pages=self._pages, routes=self._routes, ) ) return packages def _build(self, notebook: Optional[str] = None) -> None: """Compile the Bowtie application.""" if node_version() < _MIN_NODE_VERSION: raise WebpackError( f'Webpack requires at least version {_MIN_NODE_VERSION} of Node, ' f'found version {node_version}.' ) packages = self._write_templates() for filename in ['package.json', 'webpack.prod.js', 'webpack.dev.js']: if not (self._build_dir / filename).is_file(): sourcefile = self._package_dir / 'src' / filename shutil.copy(sourcefile, self._build_dir) if self._run(['yarn', '--ignore-engines', 'install'], notebook=notebook) > 1: raise YarnError('Error installing node packages') if packages: installed = self._installed_packages() new_packages = [x for x in packages if x.split('@')[0] not in installed] if new_packages: retval = self._run( ['yarn', '--ignore-engines', 'add'] + new_packages, notebook=notebook ) if retval > 1: raise YarnError('Error installing node packages') elif retval == 1: print('Yarn error but trying to continue build') retval = self._run([_WEBPACK, '--config', 'webpack.dev.js'], notebook=notebook) if retval != 0: raise WebpackError('Error building with webpack') def _endpoints(self): def generate_sio_handler(main_event, supports): # get all events from all subscriptions associated with this event uniq_events = set() for events, _ in supports: uniq_events.update(events) uniq_events.remove(main_event) for event in uniq_events: comp = COMPONENT_REGISTRY[event.uuid] if event.getter is None: raise AttributeError( f'{comp} has no getter associated with event "on_{event.name}"' ) def handler(*args): def wrapuser(): event_data = {} for event in uniq_events: comp = COMPONENT_REGISTRY[event.uuid] # we already checked that this component has a getter event_data[event.signal] = getattr(comp, event.getter)() # if there is no getter, then there is no data to unpack # if there is a getter, then we need to unpack the data sent main_getter = main_event.getter if main_getter is not None: comp = COMPONENT_REGISTRY[main_event.uuid] event_data[main_event.signal] = getattr(comp, '_' + main_getter)( msgpack.unpackb(args[0], encoding='utf8') ) # gather the remaining data from the other events through their getter methods for events, func in supports: if main_getter is not None: func(*(event_data[event.signal] for event in events)) else: func() # TODO replace with flask socketio start_background_task eventlet.spawn(copy_current_request_context(wrapuser)) return handler for event, supports in self._subscriptions.items(): self._socketio.on(event.signal)(generate_sio_handler(event, supports)) if self._init is not None: self._socketio.on('INITIALIZE')(lambda: eventlet.spawn( copy_current_request_context(self._init) )) def gen_upload(func): def upload(): upfile = request.files['file'] retval = func(upfile.filename, upfile.stream) if retval: return make_response(jsonify(), 400) return make_response(jsonify(), 200) return upload for uuid, func in self._uploads.items(): self.app.add_url_rule( f'/upload{uuid}', f'upload{uuid}', gen_upload(func), methods=['POST'] ) for page, func in self._pages.items(): # pylint: disable=protected-access self._socketio.on(f'resp#{page._uuid}')(lambda: eventlet.spawn( copy_current_request_context(func) )) # bundle route @self.app.route('/bowtie/bundle.js') def bowtiebundlejs(): # pylint: disable=unused-variable bundle_path = self.app.root_path + '/build/bundle.js' bundle_path_gz = bundle_path + '.gz' try: if os.path.getmtime(bundle_path) > os.path.getmtime(bundle_path_gz): return open(bundle_path, 'r').read() bundle = open(bundle_path_gz, 'rb').read() response = flask.make_response(bundle) response.headers['content-encoding'] = 'gzip' response.headers['vary'] = 'accept-encoding' response.headers['content-length'] = len(response.data) return response except FileNotFoundError: if os.path.isfile(bundle_path_gz): bundle = open(bundle_path_gz, 'rb').read() response = flask.make_response(bundle) response.headers['Content-Encoding'] = 'gzip' response.headers['Vary'] = 'Accept-Encoding' response.headers['Content-Length'] = len(response.data) return response return open(bundle_path, 'r').read() for schedule in self._schedules: schedule.start() def _serve(self, host='0.0.0.0', port=9991) -> None: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((host, port)) if result == 0: raise Exception(f'Port {port} is unavailable on host {host}, aborting.') self._socketio.run(self.app, host=host, port=port) for schedule in self._schedules: schedule.stop() def _installed_packages(self) -> Generator[str, None, None]: """Extract installed packages as list from `package.json`.""" with (self._build_dir / 'package.json').open('r') as f: packages = json.load(f) yield from packages['dependencies'].keys() def _create_jspath(self) -> Path: """Create the source directory for the build.""" src = self._build_dir / 'bowtiejs' os.makedirs(src, exist_ok=True) return src def _run(self, command: List[str], notebook: Optional[str] = None) -> int: """Run command from terminal and notebook and view output from subprocess.""" if notebook is None: return Popen(command, cwd=self._build_dir).wait() cmd = Popen(command, cwd=self._build_dir, stdout=PIPE, stderr=STDOUT) while True: line = cmd.stdout.readline() if line == b'' and cmd.poll() is not None: return cmd.poll() print(line.decode('utf-8'), end='') raise Exception()