class ComponentRequestHandlerTest(tornado.testing.AsyncHTTPTestCase): """Test /component endpoint.""" def tearDown(self) -> None: ComponentRegistry._instance = None def get_app(self): self.registry = ComponentRegistry() return tornado.web.Application( [ ( "/component/(.*)", ComponentRequestHandler, dict(registry=self.registry.instance()), ) ] ) def _request_component(self, path): return self.fetch("/component/%s" % path, method="GET") def test_success_request(self): """Test request success when valid parameters are provided.""" with mock.patch("streamlit.components.v1.components.os.path.isdir"): # We don't need the return value in this case. declare_component("test", path=PATH) with mock.patch( "streamlit.components.v1.components.open", mock.mock_open(read_data="Test Content"), ): response = self._request_component("components_test.test") self.assertEqual(200, response.code) self.assertEqual(b"Test Content", response.body) def test_invalid_component_request(self): """Test request failure when invalid component name is provided.""" response = self._request_component("invalid_component") self.assertEqual(404, response.code) self.assertEqual(b"invalid_component not found", response.body) def test_invalid_content_request(self): """Test request failure when invalid content (file) is provided.""" with mock.patch("streamlit.components.v1.components.os.path.isdir"): declare_component("test", path=PATH) with mock.patch("streamlit.components.v1.components.open") as m: m.side_effect = OSError("Invalid content") response = self._request_component("components_test.test") self.assertEqual(404, response.code) self.assertEqual( b"components_test.test read error: Invalid content", response.body, )
def test_only_url(self): """Succeed when a URL is provided.""" component = components.declare_component("test", url=URL) self.assertEqual(URL, component.url) self.assertIsNone(component.path) self.assertEqual( ComponentRegistry.instance().get_component_path("components_test"), component.abspath, )
def test_register_invalid_path(self): """We raise an exception if a component is registered with a non-existent path. """ test_path = "/a/test/component/directory" registry = ComponentRegistry.instance() with self.assertRaises(StreamlitAPIException) as ctx: registry.register_component(CustomComponent("test_component", test_path)) self.assertIn("No such component directory", ctx.exception)
def test_register_component_no_path(self): """It's not an error to register a component without a path.""" registry = ComponentRegistry.instance() # Return None when the component hasn't been registered self.assertIsNone(registry.get_component_path("test_component")) # And also return None when the component doesn't have a path registry.register_component( CustomComponent("test_component", url="http://not.a.url")) self.assertIsNone(registry.get_component_path("test_component"))
def test_register_component_with_path(self): """Registering a component should associate it with its path.""" test_path = "/a/test/component/directory" def isdir(path): return path == test_path registry = ComponentRegistry.instance() with mock.patch("streamlit.components.v1.components.os.path.isdir", side_effect=isdir): registry.register_component( CustomComponent("test_component", path=test_path)) self.assertEqual(test_path, registry.get_component_path("test_component"))
def test_only_path(self): """Succeed when a path is provided.""" def isdir(path): return path == PATH or path == os.path.abspath(PATH) with mock.patch("streamlit.components.v1.components.os.path.isdir", side_effect=isdir): component = components.declare_component("test", path=PATH) self.assertEqual(PATH, component.path) self.assertIsNone(component.url) self.assertEqual( ComponentRegistry.instance().get_component_path(component.name), component.abspath, )
def test_register_duplicate_path(self): """It's not an error to re-register a component. (This can happen during development). """ test_path_1 = "/a/test/component/directory" test_path_2 = "/another/test/component/directory" def isdir(path): return path in (test_path_1, test_path_2) registry = ComponentRegistry.instance() with mock.patch( "streamlit.components.v1.components.os.path.isdir", side_effect=isdir ): registry.register_component(CustomComponent("test_component", test_path_1)) registry.register_component(CustomComponent("test_component", test_path_1)) self.assertEqual(test_path_1, registry.get_component_path("test_component")) registry.register_component(CustomComponent("test_component", test_path_2)) self.assertEqual(test_path_2, registry.get_component_path("test_component"))
def _create_app(self) -> tornado.web.Application: """Create our tornado web app.""" base = config.get_option("server.baseUrlPath") routes = [ ( make_url_path_regex(base, "stream"), _BrowserWebSocketHandler, dict(server=self), ), ( make_url_path_regex(base, "healthz"), HealthHandler, dict(callback=lambda: self.is_ready_for_browser_connection), ), (make_url_path_regex(base, "debugz"), DebugHandler, dict(server=self)), (make_url_path_regex(base, "metrics"), MetricsHandler), ( make_url_path_regex(base, "message"), MessageCacheHandler, dict(cache=self._message_cache), ), ( make_url_path_regex( base, UPLOAD_FILE_ROUTE, ), UploadFileRequestHandler, dict( file_mgr=self._uploaded_file_mgr, get_session_info=self._get_session_info, ), ), ( make_url_path_regex(base, "assets/(.*)"), AssetsFileHandler, {"path": "%s/" % file_util.get_assets_dir()}, ), (make_url_path_regex(base, "media/(.*)"), MediaFileHandler, {"path": ""}), ( make_url_path_regex(base, "component/(.*)"), ComponentRequestHandler, dict(registry=ComponentRegistry.instance()), ), ] if config.get_option("server.scriptHealthCheckEnabled"): routes.extend( [ ( make_url_path_regex(base, "script-health-check"), HealthHandler, dict(callback=lambda: self.does_script_run_without_error()), ) ] ) if config.get_option("global.developmentMode"): LOGGER.debug("Serving static content from the Node dev server") else: static_path = file_util.get_static_dir() LOGGER.debug("Serving static content from %s", static_path) routes.extend( [ ( make_url_path_regex(base, "(.*)"), StaticFileHandler, {"path": "%s/" % static_path, "default_filename": "index.html"}, ), (make_url_path_regex(base, trailing_slash=False), AddSlashHandler), ] ) return tornado.web.Application( routes, # type: ignore[arg-type] cookie_secret=config.get_option("server.cookieSecret"), xsrf_cookies=config.get_option("server.enableXsrfProtection"), **TORNADO_SETTINGS, # type: ignore[arg-type] )
class ComponentRequestHandlerTest(tornado.testing.AsyncHTTPTestCase): """Test /component endpoint.""" def tearDown(self) -> None: ComponentRegistry._instance = None def get_app(self): self.registry = ComponentRegistry() return tornado.web.Application([( "/component/(.*)", ComponentRequestHandler, dict(registry=self.registry.instance()), )]) def _request_component(self, path): return self.fetch("/component/%s" % path, method="GET") def test_success_request(self): """Test request success when valid parameters are provided.""" with mock.patch("streamlit.components.v1.components.os.path.isdir"): # We don't need the return value in this case. declare_component("test", path=PATH) with mock.patch( "streamlit.components.v1.components.open", mock.mock_open(read_data="Test Content"), ): response = self._request_component("components_test.test") self.assertEqual(200, response.code) self.assertEqual(b"Test Content", response.body) def test_invalid_component_request(self): """Test request failure when invalid component name is provided.""" response = self._request_component("invalid_component") self.assertEqual(404, response.code) self.assertEqual(b"not found", response.body) def test_invalid_content_request(self): """Test request failure when invalid content (file) is provided.""" with mock.patch("streamlit.components.v1.components.os.path.isdir"): declare_component("test", path=PATH) with mock.patch("streamlit.components.v1.components.open") as m: m.side_effect = OSError("Invalid content") response = self._request_component("components_test.test") self.assertEqual(404, response.code) self.assertEqual( b"read error", response.body, ) def test_support_binary_files_request(self): """Test support for binary files reads.""" def _open_read(m, payload): is_binary = False args, kwargs = m.call_args if len(args) > 1: if "b" in args[1]: is_binary = True encoding = "utf-8" if "encoding" in kwargs: encoding = kwargs["encoding"] if is_binary: from io import BytesIO return BytesIO(payload) else: from io import TextIOWrapper return TextIOWrapper(str(payload, encoding=encoding)) with mock.patch("streamlit.components.v1.components.os.path.isdir"): declare_component("test", path=PATH) payload = b"\x00\x01\x00\x00\x00\x0D\x00\x80" # binary non utf-8 payload with mock.patch("streamlit.components.v1.components.open") as m: m.return_value.__enter__ = lambda _: _open_read(m, payload) response = self._request_component("components_test.test") self.assertEqual(200, response.code) self.assertEqual( payload, response.body, )
def _create_app(self): """Create our tornado web app. Returns ------- tornado.web.Application """ base = config.get_option("server.baseUrlPath") routes = [ ( make_url_path_regex(base, "stream"), _BrowserWebSocketHandler, dict(server=self), ), ( make_url_path_regex(base, "healthz"), HealthHandler, dict(callback=lambda: self.is_ready_for_browser_connection), ), (make_url_path_regex(base, "debugz"), DebugHandler, dict(server=self)), (make_url_path_regex(base, "metrics"), MetricsHandler), ( make_url_path_regex(base, "message"), MessageCacheHandler, dict(cache=self._message_cache), ), ( # Tornado doesn't allow body in DELETE requests. # Passing lookup values in URL make_url_path_regex( base, "/upload_file/(?P<session_id>.*)/(?P<widget_id>.*)/(?P<file_id>[0-9]*)?", ), UploadFileRequestHandler, dict(file_mgr=self._uploaded_file_mgr), ), ( make_url_path_regex(base, "upload_file"), UploadFileRequestHandler, dict(file_mgr=self._uploaded_file_mgr), ), ( make_url_path_regex(base, "assets/(.*)"), AssetsFileHandler, { "path": "%s/" % file_util.get_assets_dir() }, ), (make_url_path_regex(base, "media/(.*)"), MediaFileHandler, { "path": "" }), ( make_url_path_regex(base, "component/(.*)"), ComponentRequestHandler, dict(registry=ComponentRegistry.instance()), ), ] if config.get_option("global.developmentMode"): LOGGER.debug("Serving static content from the Node dev server") else: static_path = file_util.get_static_dir() LOGGER.debug("Serving static content from %s", static_path) routes.extend([ ( make_url_path_regex(base, "(.*)"), StaticFileHandler, { "path": "%s/" % static_path, "default_filename": "index.html" }, ), (make_url_path_regex(base, trailing_slash=False), AddSlashHandler), ]) return tornado.web.Application( routes, # type: ignore[arg-type] cookie_secret=config.get_option("server.cookieSecret"), xsrf_cookies=config.get_option("server.enableXsrfProtection"), **TORNADO_SETTINGS, # type: ignore[arg-type] )