Exemple #1
0
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()
Exemple #2
0
    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'})
Exemple #3
0
    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'})
Exemple #4
0
    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'})
Exemple #5
0
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)
Exemple #6
0
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!'})
Exemple #7
0
    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)
Exemple #8
0
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)
Exemple #9
0
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)
Exemple #10
0
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
Exemple #11
0
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()
Exemple #12
0
    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":
Exemple #13
0
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)
Exemple #14
0
    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)





Exemple #15
0
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)

Exemple #16
0
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)
Exemple #17
0
                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)
Exemple #19
0
@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())
Exemple #20
0
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()
Exemple #21
0
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)
Exemple #22
0
    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'])
Exemple #23
0
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()