def test_pull_large_document(self, ManagedServerLoop) -> None: application = Application() def add_roots(doc): import numpy as np rows, cols = (40000, 100) columns=['x'+str(i) for i in range(cols)] a = np.random.randn(cols, rows) source = ColumnDataSource(data=dict(zip(columns, a))) doc.add_root(source) handler = FunctionHandler(add_roots) application.add(handler) with ManagedServerLoop(application) as server: client_session = pull_session(session_id='test_pull_document', url=url(server), io_loop=server.io_loop, max_message_size=50000000) assert len(client_session.document.roots) == 1 server_session = server.get_session('/', client_session.id) assert len(server_session.document.roots) == 1 results = {} for r in server_session.document.roots: if hasattr(r, 'data'): results['data'] = r.data assert len(list(results['data'].keys())) == 100 assert all(len(x) == 40000 for x in results['data'].values()) client_session.close() client_session._loop_until_closed() assert not client_session.connected
def test_pull_document(self): application = Application() def add_roots(doc): doc.add_root(AnotherModelInTestClientServer(bar=43)) doc.add_root(SomeModelInTestClientServer(foo=42)) handler = FunctionHandler(add_roots) application.add(handler) with ManagedServerLoop(application) as server: client_session = pull_session(session_id='test_pull_document', url=server.ws_url, io_loop=server.io_loop) assert len(client_session.document.roots) == 2 server_session = server.get_session('/', client_session.id) assert len(server_session.document.roots) == 2 results = {} for r in server_session.document.roots: if hasattr(r, 'foo'): results['foo'] = r.foo if hasattr(r, 'bar'): results['bar'] = r.bar assert results['foo'] == 42 assert results['bar'] == 43 client_session.close() client_session.loop_until_closed() assert not client_session.connected
def build_applications(self, args): if args.files: files = args.files else: files = [] applications = {} for file in files: file = os.path.abspath(file) if os.path.isdir(file): handler = DirectoryHandler(filename=file) else: handler = ScriptHandler(filename=file) application = Application() application.add(handler) route = handler.url_path() if not route: if '/' in applications: die("Don't know the URL path to use for %s" % (file)) route = '/' applications[route] = application if len(applications) == 0: # create an empty application by default, used with output_server typically applications['/'] = Application() return applications
def test_pull_document(self, ManagedServerLoop: MSL) -> None: application = Application() def add_roots(doc: Document): doc.add_root(AnotherModelInTestClientServer(bar=43)) doc.add_root( SomeModelInTestClientServer(foo=42, data=bytes( [0x00, 0x01, 0xFE, 0xFF]))) handler = FunctionHandler(add_roots) application.add(handler) with ManagedServerLoop(application) as server: client_session = pull_session(session_id=ID("test_pull_document"), url=url(server), io_loop=server.io_loop) assert len(client_session.document.roots) == 2 server_session = server.get_session('/', client_session.id) assert len(server_session.document.roots) == 2 results = {} for r in server_session.document.roots: if hasattr(r, 'foo'): results['foo'] = r.foo if hasattr(r, 'bar'): results['bar'] = r.bar assert results['foo'] == 42 assert results['bar'] == 43 client_session.close() client_session._loop_until_closed() assert not client_session.connected
def test_pull_document(self): application = Application() def add_roots(doc): doc.add_root(AnotherModelInTestClientServer(bar=43)) doc.add_root(SomeModelInTestClientServer(foo=42)) handler = FunctionHandler(add_roots) application.add(handler) with ManagedServerLoop(application) as server: client_session = pull_session(session_id='test_pull_document', url=url(server), io_loop=server.io_loop) assert len(client_session.document.roots) == 2 server_session = server.get_session('/', client_session.id) assert len(server_session.document.roots) == 2 results = {} for r in server_session.document.roots: if hasattr(r, 'foo'): results['foo'] = r.foo if hasattr(r, 'bar'): results['bar'] = r.bar assert results['foo'] == 42 assert results['bar'] == 43 client_session.close() client_session.loop_until_closed(suppress_warning=True) assert not client_session.connected
def build_applications(self, args): if args.files: files = args.files else: files = [] applications = {} for file in files: file = os.path.abspath(file) if os.path.isdir(file): handler = DirectoryHandler(filename=file) else: handler = ScriptHandler(filename=file) application = Application() application.add(handler) route = handler.url_path() if not route: if '/' in applications: die("Don't know the URL path to use for %s" % (file)) route = '/' applications[route] = application if len(applications) == 0: # create an empty application by default, used with output_server typically applications['/'] = Application() return applications
def test_one_handler(): a = Application() def add_roots(doc): doc.add_root(AnotherModelInTestApplication()) doc.add_root(SomeModelInTestApplication()) handler = FunctionHandler(add_roots) a.add(handler) doc = a.create_document() assert len(doc.roots) == 2
def test_one_handler(self): a = Application() def add_roots(doc): doc.add_root(AnotherModelInTestApplication()) doc.add_root(SomeModelInTestApplication()) handler = FunctionHandler(add_roots) a.add(handler) doc = a.create_document() assert len(doc.roots) == 2
def test_no_static_path(): a = Application() def add_roots(doc): doc.add_root(AnotherModelInTestApplication()) doc.add_root(SomeModelInTestApplication()) def add_one_root(doc): doc.add_root(AnotherModelInTestApplication()) handler = FunctionHandler(add_roots) a.add(handler) handler2 = FunctionHandler(add_one_root) a.add(handler2) assert a.static_path == None
def test__lifecycle_hooks(ManagedServerLoop: MSL) -> None: application = Application() handler = HookTestHandler() application.add(handler) with ManagedServerLoop(application, check_unused_sessions_milliseconds=30) as server: client_session = pull_session(session_id=ID("test__lifecycle_hooks"), url=url(server), io_loop=server.io_loop) client_doc = client_session.document assert len(client_doc.roots) == 1 server_session = server.get_session('/', client_session.id) server_doc = server_session.document assert len(server_doc.roots) == 1 # save for later, since doc.roots will be emptied after the session is closed client_hook_list = list(client_doc.roots[0].hooks) server_hook_list = list(server_doc.roots[0].hooks) client_session.close() # expire the session quickly rather than after the usual timeout server_session.request_expiration() server.io_loop.call_later(0.1, lambda: server.io_loop.stop()) server.io_loop.start() assert handler.hooks == [ "server_loaded", "session_created", "modify", "next_tick", "timeout", "periodic", "session_destroyed", "server_unloaded", ] assert handler.load_count == 1 assert handler.unload_count == 1 # 3 instead of 6, because locked callbacks on destroyed sessions become no-ops assert handler.session_creation_async_value == 3 assert client_doc.title == "Modified" assert server_doc.title == "Modified" # only the handler sees "session_destroyed" since the session is shut down at that point. assert client_hook_list == ["session_created", "modify"] assert server_hook_list == ["session_created", "modify"]
def test_excess_static_path(): a = Application() def add_roots(doc): doc.add_root(AnotherModelInTestApplication()) doc.add_root(SomeModelInTestApplication()) def add_one_root(doc): doc.add_root(AnotherModelInTestApplication()) handler = FunctionHandler(add_roots) handler._static = "foo" a.add(handler) handler2 = FunctionHandler(add_one_root) handler2._static = "bar" with pytest.raises(RuntimeError): a.add(handler2)
def test_excess_static_path(): a = Application() def add_roots(doc): doc.add_root(AnotherModelInTestApplication()) doc.add_root(SomeModelInTestApplication()) def add_one_root(doc): doc.add_root(AnotherModelInTestApplication()) handler = FunctionHandler(add_roots) handler._static = "foo" a.add(handler) handler2 = FunctionHandler(add_one_root) handler2._static = "bar" with pytest.raises(RuntimeError) as e: a.add(handler2) assert "More than one static path" in str(e)
def test_lots_of_concurrent_messages(self): application = Application() def setup_stuff(doc): m1 = AnotherModelInTestClientServer(bar=43, name='m1') m2 = SomeModelInTestClientServer(foo=42, name='m2') m3 = SomeModelInTestClientServer(foo=68, name='m3') doc.add_root(m1) doc.add_root(m2) doc.add_root(m3) def timeout1(): m1.bar += 1 timeout1_cb_id = doc.add_timeout_callback(timeout1, 1) def timeout2(): m2.foo += 1 timeout2_cb_id = doc.add_timeout_callback(timeout2, 3) def periodic1(): m1.bar += 1 doc.remove_timeout_callback(timeout1_cb_id) doc.add_timeout_callback(timeout1, m1.bar % 7) doc.add_periodic_callback(periodic1, 3) def periodic2(): m2.foo += 1 doc.remove_timeout_callback(timeout2_cb_id) doc.add_timeout_callback(timeout2, m2.foo % 7) doc.add_periodic_callback(periodic2, 1) def server_on_change(event): if isinstance(event, ModelChangedEvent) and event.model is m3: return m3.foo += 1 doc.on_change(server_on_change) handler = FunctionHandler(setup_stuff) application.add(handler) # keep_alive_milliseconds=1 sends pings as fast as the OS will let us with ManagedServerLoop(application, keep_alive_milliseconds=1) as server: session = pull_session( session_id='test_lots_of_concurrent_messages', url=url(server), io_loop=server.io_loop) assert session.connected server_session = server.get_session('/', session.id) def client_timeout(): m = session.document.roots[0] m.name = m.name[::-1] cb_id = session.document.add_timeout_callback(client_timeout, 3) def client_periodic(): m = session.document.roots[1] m.name = m.name[::-1] session.document.remove_timeout_callback(cb_id) session.document.add_timeout_callback(client_timeout, 3) session.document.add_periodic_callback(client_periodic, 1) result = {} def end_test(): result['connected'] = session.connected result[ 'server_connection_count'] = server_session.connection_count result['server_close_code'] = next( iter(server._tornado._clients))._socket.close_code result['doc'] = session.document.to_json() session.close() # making this longer is more likely to trigger bugs, but it also # makes the test take however much time you put here session.document.add_timeout_callback(end_test, 250) def client_on_change(event): if not isinstance(event, TitleChangedEvent): session.document.title = session.document.title[::-1] session.document.on_change(client_on_change) session.loop_until_closed(suppress_warning=True) assert not session.connected # we should have still been connected at the end, # if we didn't have any crazy protocol errors assert 'connected' in result assert result['connected'] # server should also still have been connected assert result['server_connection_count'] == 1 assert result['server_close_code'] is None
def test__lifecycle_hooks(): application = Application() handler = HookTestHandler() application.add(handler) with ManagedServerLoop(application, check_unused_sessions_milliseconds=30) as server: # wait for server callbacks to run before we mix in the # session, this keeps the test deterministic def check_done(): if len(handler.hooks) == 4: server.io_loop.stop() server_load_checker = PeriodicCallback(check_done, 1, io_loop=server.io_loop) server_load_checker.start() server.io_loop.start() server_load_checker.stop() # now we create a session client_session = pull_session(session_id='test__lifecycle_hooks', url=url(server), io_loop=server.io_loop) client_doc = client_session.document assert len(client_doc.roots) == 1 server_session = server.get_session('/', client_session.id) server_doc = server_session.document assert len(server_doc.roots) == 1 client_session.close() # expire the session quickly rather than after the # usual timeout server_session.request_expiration() def on_done(): server.io_loop.stop() server.io_loop.call_later(0.1, on_done) server.io_loop.start() assert handler.hooks == [ "server_loaded", "next_tick_server", "timeout_server", "periodic_server", "session_created", "next_tick_session", "modify", "timeout_session", "periodic_session", "session_destroyed", "server_unloaded" ] client_hook_list = client_doc.roots[0] server_hook_list = server_doc.roots[0] assert handler.load_count == 1 assert handler.unload_count == 1 assert handler.session_creation_async_value == 6 assert client_doc.title == "Modified" assert server_doc.title == "Modified" # the client session doesn't see the event that adds "session_destroyed" since # we shut down at that point. assert client_hook_list.hooks == ["session_created", "modify"] assert server_hook_list.hooks == [ "session_created", "modify", "session_destroyed" ]
def test__lifecycle_hooks(ManagedServerLoop) -> None: application = Application() handler = HookTestHandler() application.add(handler) with ManagedServerLoop(application, check_unused_sessions_milliseconds=30) as server: # wait for server callbacks to run before we mix in the # session, this keeps the test deterministic def check_done(): if len(handler.hooks) == 4: server.io_loop.stop() server_load_checker = PeriodicCallback(check_done, 1) server_load_checker.start() server.io_loop.start() server_load_checker.stop() # now we create a session client_session = pull_session(session_id='test__lifecycle_hooks', url=url(server), io_loop=server.io_loop) client_doc = client_session.document assert len(client_doc.roots) == 1 server_session = server.get_session('/', client_session.id) server_doc = server_session.document assert len(server_doc.roots) == 1 # we have to capture these here for examination later, since after # the session is closed, doc.roots will be emptied client_hook_list = client_doc.roots[0] server_hook_list = server_doc.roots[0] client_session.close() # expire the session quickly rather than after the # usual timeout server_session.request_expiration() def on_done(): server.io_loop.stop() server.io_loop.call_later(0.1, on_done) server.io_loop.start() assert handler.hooks == ["server_loaded", "next_tick_server", "timeout_server", "periodic_server", "session_created", "modify", "next_tick_session", "timeout_session", "periodic_session", "session_destroyed", "server_unloaded"] assert handler.load_count == 1 assert handler.unload_count == 1 # this is 3 instead of 6 because locked callbacks on destroyed sessions # are turned into no-ops assert handler.session_creation_async_value == 3 assert client_doc.title == "Modified" assert server_doc.title == "Modified" # only the handler sees the event that adds "session_destroyed" since # the session is shut down at that point. assert client_hook_list.hooks == ["session_created", "modify"] assert server_hook_list.hooks == ["session_created", "modify"]
def on_transfer_function_change(self, attr, old, new): self.model.transfer_function = self.model.transfer_functions[new] self.update_image() def on_opacity_slider_change(self, attr, old, new): for renderer in self.fig.renderers: if hasattr(renderer, 'image_source'): renderer.alpha = new / 100 if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--config', help='yaml config file (e.g. nyc_taxi.yml)', required=True) args = vars(parser.parse_args()) def add_roots(doc): model = AppState(args['config']) view = AppView(model) GetDataset.model = model doc.add_root(view.layout) app = Application() app.add(FunctionHandler(add_roots)) # Start server object wired to bokeh client. Instantiating ``Server`` # directly is used to add custom http endpoint into ``extra_patterns``. server = Server(app, io_loop=IOLoop(), extra_patterns=[(r"/datashader", GetDataset)], port=5000) print('Starting server at http://localhost:5000/...') server.start()
def test_lots_of_concurrent_messages(self): application = Application() def setup_stuff(doc): m1 = AnotherModelInTestClientServer(bar=43, name='m1') m2 = SomeModelInTestClientServer(foo=42, name='m2') m3 = SomeModelInTestClientServer(foo=68, name='m3') doc.add_root(m1) doc.add_root(m2) doc.add_root(m3) def timeout1(): m1.bar += 1 doc.add_timeout_callback(timeout1, 1) def timeout2(): m2.foo +=1 doc.add_timeout_callback(timeout2, 3) def periodic1(): m1.bar += 1 doc.remove_timeout_callback(timeout1) doc.add_timeout_callback(timeout1, m1.bar % 7) doc.add_periodic_callback(periodic1, 3) def periodic2(): m2.foo += 1 doc.remove_timeout_callback(timeout2) doc.add_timeout_callback(timeout2, m2.foo % 7) doc.add_periodic_callback(periodic2, 1) def server_on_change(event): if isinstance(event, ModelChangedEvent) and event.model is m3: return m3.foo += 1 doc.on_change(server_on_change) handler = FunctionHandler(setup_stuff) application.add(handler) # keep_alive_milliseconds=1 sends pings as fast as the OS will let us with ManagedServerLoop(application, keep_alive_milliseconds=1) as server: session = pull_session(session_id='test_lots_of_concurrent_messages', url=url(server), io_loop=server.io_loop) assert session.connected server_session = server.get_session('/', session.id) def client_timeout(): m = session.document.roots[0] m.name = m.name[::-1] session.document.add_timeout_callback(client_timeout, 3) def client_periodic(): m = session.document.roots[1] m.name = m.name[::-1] session.document.remove_timeout_callback(client_timeout) session.document.add_timeout_callback(client_timeout, 3) session.document.add_periodic_callback(client_periodic, 1) result = {} def end_test(): result['connected'] = session.connected result['server_connection_count'] = server_session.connection_count result['server_close_code'] = next(iter(server._tornado._clients))._socket.close_code result['doc'] = session.document.to_json() session.close() # making this longer is more likely to trigger bugs, but it also # makes the test take however much time you put here session.document.add_timeout_callback(end_test, 250) def client_on_change(event): if not isinstance(event, TitleChangedEvent): session.document.title = session.document.title[::-1] session.document.on_change(client_on_change) session.loop_until_closed() assert not session.connected # we should have still been connected at the end, # if we didn't have any crazy protocol errors assert 'connected' in result assert result['connected'] # server should also still have been connected assert result['server_connection_count'] == 1 assert result['server_close_code'] is None
help= 'use out-of-core processing if available, for datasets larger than memory', default=False, action='store_true') args = vars(parser.parse_args()) APP_PORT = args['port'] def add_roots(doc): model = AppState(args['config'], args['outofcore'], APP_PORT) view = AppView(model) GetDataset.model = model doc.add_root(view.layout) app = Application() app.add(FunctionHandler(add_roots)) # Start server object wired to bokeh client. Instantiating ``Server`` # directly is used to add custom http endpoint into ``extra_patterns``. url = 'http://localhost:{}/'.format(APP_PORT) print('Starting server at {}...'.format(url)) server = Server(app, io_loop=IOLoop(), extra_patterns=[(r"/datashader", GetDataset)], port=APP_PORT) try: webbrowser.open(url) except: msg = '''Unable to open web browser; please navigate to port {} on the machine where the server is running (which may first need to be forwarded to your local machine if the server is running remotely)
def test__lifecycle_hooks(): application = Application() handler = HookTestHandler() application.add(handler) with ManagedServerLoop(application, check_unused_sessions_milliseconds=30) as server: # wait for server callbacks to run before we mix in the # session, this keeps the test deterministic def check_done(): if len(handler.hooks) == 4: server.io_loop.stop() server_load_checker = PeriodicCallback(check_done, 1) server_load_checker.start() server.io_loop.start() server_load_checker.stop() # now we create a session client_session = pull_session(session_id='test__lifecycle_hooks', url=url(server), io_loop=server.io_loop) client_doc = client_session.document assert len(client_doc.roots) == 1 server_session = server.get_session('/', client_session.id) server_doc = server_session.document assert len(server_doc.roots) == 1 # we have to capture these here for examination later, since after # the session is closed, doc.roots will be emptied client_hook_list = client_doc.roots[0] server_hook_list = server_doc.roots[0] client_session.close() # expire the session quickly rather than after the # usual timeout server_session.request_expiration() def on_done(): server.io_loop.stop() server.io_loop.call_later(0.1, on_done) server.io_loop.start() assert handler.hooks == ["server_loaded", "next_tick_server", "timeout_server", "periodic_server", "session_created", "next_tick_session", "modify", "timeout_session", "periodic_session", "session_destroyed", "server_unloaded"] assert handler.load_count == 1 assert handler.unload_count == 1 # this is 3 instead of 6 because locked callbacks on destroyed sessions # are turned into no-ops assert handler.session_creation_async_value == 3 assert client_doc.title == "Modified" assert server_doc.title == "Modified" # only the handler sees the event that adds "session_destroyed" since # the session is shut down at that point. assert client_hook_list.hooks == ["session_created", "modify"] assert server_hook_list.hooks == ["session_created", "modify"]
def _fixup(self, app: Application) -> Application: if not any( isinstance(handler, DocumentLifecycleHandler) for handler in app.handlers): app.add(DocumentLifecycleHandler()) return app
def customize_kwargs(self, args, server_kwargs): '''Allows subclasses to customize ``server_kwargs``. Should modify and return a copy of the ``server_kwargs`` dictionary. ''' kwargs = dict(server_kwargs) if 'index' not in kwargs: kwargs['index'] = INDEX_HTML # Handle tranquilized functions in the supplied functions kwargs['extra_patterns'] = patterns = kwargs.get('extra_patterns', []) static_dirs = parse_vars(args.static_dirs) if args.static_dirs else {} patterns += get_static_routes(static_dirs) files = [] for f in args.files: if args.glob: files.extend(glob(f)) else: files.append(f) if args.index and not args.index.endswith('.html'): found = False for ext in self._extensions: index = args.index if args.index.endswith( ext) else f'{args.index}{ext}' if any(f.endswith(index) for f in files): found = True if not found: raise ValueError( "The --index argument must either specify a jinja2 " "template with a .html file extension or select one " "of the applications being served as the default. " f"The specified application {index!r} could not be " "found.") # Handle tranquilized functions in the supplied functions if args.rest_provider in REST_PROVIDERS: pattern = REST_PROVIDERS[args.rest_provider](files, args.rest_endpoint) patterns.extend(pattern) elif args.rest_provider is not None: raise ValueError("rest-provider %r not recognized." % args.rest_provider) config.autoreload = args.autoreload if config.autoreload: for f in files: watch(f) if args.setup: setup_path = args.setup with open(setup_path) as f: setup_source = f.read() nodes = ast.parse(setup_source, os.fspath(setup_path)) code = compile(nodes, filename=setup_path, mode='exec', dont_inherit=True) module_name = 'panel_setup_module' module = ModuleType(module_name) module.__dict__['__file__'] = fullpath(setup_path) exec(code, module.__dict__) state._setup_module = module if args.warm or args.autoreload: argvs = {f: args.args for f in files} applications = build_single_handler_applications(files, argvs) if args.autoreload: with record_modules(): for app in applications.values(): doc = app.create_document() with set_curdoc(doc): state._on_load(None) _cleanup_doc(doc) else: for app in applications.values(): doc = app.create_document() with set_curdoc(doc): state._on_load(None) _cleanup_doc(doc) prefix = args.prefix if prefix is None: prefix = "" prefix = prefix.strip("/") if prefix: prefix = "/" + prefix config.profiler = args.profiler if args.admin: from ..io.admin import admin_panel from ..io.server import per_app_patterns config._admin = True app = Application(FunctionHandler(admin_panel)) unused_timeout = args.check_unused_sessions or 15000 app_ctx = AdminApplicationContext(app, unused_timeout=unused_timeout, url='/admin') if all(not isinstance(handler, DocumentLifecycleHandler) for handler in app._handlers): app.add(DocumentLifecycleHandler()) app_patterns = [] for p in per_app_patterns: route = '/admin' + p[0] context = {"application_context": app_ctx} route = prefix + route app_patterns.append((route, p[1], context)) websocket_path = None for r in app_patterns: if r[0].endswith("/ws"): websocket_path = r[0] if not websocket_path: raise RuntimeError("Couldn't find websocket path") for r in app_patterns: r[2]["bokeh_websocket_path"] = websocket_path try: import snakeviz SNAKEVIZ_PATH = os.path.join( os.path.dirname(snakeviz.__file__), 'static') app_patterns.append( ('/snakeviz/static/(.*)', StaticFileHandler, dict(path=SNAKEVIZ_PATH))) except Exception: pass patterns.extend(app_patterns) config.session_history = args.session_history if args.rest_session_info: pattern = REST_PROVIDERS['param'](files, 'rest') patterns.extend(pattern) state.publish('session_info', state, ['session_info']) if args.num_threads is not None: if config.nthreads is not None: raise ValueError( "Supply num_threads either using the environment variable " "PANEL_NUM_THREADS or as an explicit argument, not both.") config.nthreads = args.num_threads if args.oauth_provider: config.oauth_provider = args.oauth_provider config.oauth_expiry = args.oauth_expiry_days if config.oauth_key and args.oauth_key: raise ValueError( "Supply OAuth key either using environment variable " "or via explicit argument, not both.") elif args.oauth_key: config.oauth_key = args.oauth_key elif not config.oauth_key: raise ValueError( "When enabling an OAuth provider you must supply " "a valid oauth_key either using the --oauth-key " "CLI argument or PANEL_OAUTH_KEY environment " "variable.") if config.oauth_secret and args.oauth_secret: raise ValueError( "Supply OAuth secret either using environment variable " "or via explicit argument, not both.") elif args.oauth_secret: config.oauth_secret = args.oauth_secret elif not config.oauth_secret: raise ValueError( "When enabling an OAuth provider you must supply " "a valid OAuth secret either using the --oauth-secret " "CLI argument or PANEL_OAUTH_SECRET environment " "variable.") if args.oauth_extra_params: config.oauth_extra_params = ast.literal_eval( args.oauth_extra_params) if config.oauth_encryption_key and args.oauth_encryption_key: raise ValueError( "Supply OAuth encryption key either using environment " "variable or via explicit argument, not both.") elif args.oauth_encryption_key: encryption_key = args.oauth_encryption_key.encode('ascii') try: key = base64.urlsafe_b64decode(encryption_key) except Exception: raise ValueError( "OAuth encryption key was not a valid base64 " "string. Generate an encryption key with " "`panel oauth-secret` and ensure you did not " "truncate the returned string.") if len(key) != 32: raise ValueError( "OAuth encryption key must be 32 url-safe " "base64-encoded bytes.") config.oauth_encryption_key = encryption_key else: print("WARNING: OAuth has not been configured with an " "encryption key and will potentially leak " "credentials in cookies and a JWT token embedded " "in the served website. Use at your own risk or " "generate a key with the `panel oauth-key` CLI " "command and then provide it to `panel serve` " "using the PANEL_OAUTH_ENCRYPTION environment " "variable or the --oauth-encryption-key CLI " "argument.") if config.oauth_encryption_key: try: from cryptography.fernet import Fernet except ImportError: raise ImportError( "Using OAuth2 provider with Panel requires the " "cryptography library. Install it with `pip install " "cryptography` or `conda install cryptography`.") state.encryption = Fernet(config.oauth_encryption_key) if args.cookie_secret and config.cookie_secret: raise ValueError( "Supply cookie secret either using environment " "variable or via explicit argument, not both.") elif args.cookie_secret: config.cookie_secret = args.cookie_secret else: raise ValueError( "When enabling an OAuth provider you must supply " "a valid cookie_secret either using the --cookie-secret " "CLI argument or the PANEL_COOKIE_SECRET environment " "variable.") kwargs['auth_provider'] = OAuthProvider( error_template=args.oauth_error_template) if args.oauth_redirect_uri and config.oauth_redirect_uri: raise ValueError( "Supply OAuth redirect URI either using environment " "variable or via explicit argument, not both.") elif args.oauth_redirect_uri: config.oauth_redirect_uri = args.oauth_redirect_uri if args.oauth_jwt_user and config.oauth_jwt_user: raise ValueError( "Supply OAuth JWT user either using environment " "variable or via explicit argument, not both.") elif args.oauth_jwt_user: config.oauth_jwt_user = args.oauth_jwt_user if config.cookie_secret: kwargs['cookie_secret'] = config.cookie_secret return kwargs