mirror of
https://github.com/cisagov/ScubaGoggles.git
synced 2026-10-16 04:16:51 +02:00
ui command: remove required dependency on streamlit (#1141)
Run Link Checker for Cron Job / markdown-link-check (push) Has been cancelled
Run Smoke Test / configuration (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Pylint (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Run OPA Tests (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Run Markdown Link Check (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Run Python Tests (push) Has been cancelled
Run Smoke Test / smoke-test (push) Has been cancelled
Run Smoke Test / lhci (push) Has been cancelled
Run Link Checker for Cron Job / markdown-link-check (push) Has been cancelled
Run Smoke Test / configuration (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Pylint (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Run OPA Tests (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Run Markdown Link Check (push) Has been cancelled
Run CI Pipeline for ScubaGoggles / Run Python Tests (push) Has been cancelled
Run Smoke Test / smoke-test (push) Has been cancelled
Run Smoke Test / lhci (push) Has been cancelled
ui launch: use config options to silence streamlit; change streamlit install command pyproject: use optional dependencies to install UI packages update unit tests for UI changes
This commit is contained in:
1 parent
8479d7c52b
commit
65d4e287ce
8 files changed
+57
-69
No files matched your search
@@ -76,14 +76,27 @@ running ScubaGoggles.
|
||||
> [!NOTE]
|
||||
> If you are interested in contributing to ScubaGoggles or for any reason need to install the latest development code, please see the [Development Guide](../development/DEVELOPMENTGUIDE.md) for instructions on setting up ScubaGoggles for development. Otherwise, continue with the instructions below.
|
||||
>
|
||||
>pip is not installed by default on LinuxOS. If pip is not installed in your environment, please visit [pip website](https://pip.pypa.io/en/stable/installation/) for instructions on how to download and install pip.
|
||||
>pip may not be installed by default on LinuxOS. If pip is not installed in your environment, please visit [pip website](https://pip.pypa.io/en/stable/installation/) for instructions on how to download and install pip.
|
||||
|
||||
To install ScubaGoggles:
|
||||
1. If you are using a virtual environment, ensure your environment is activated by running the appropriate command from the previous section.
|
||||
2. Next, run the following command. Depending on your system, you may also need to substitute `pip` with `pip3`.
|
||||
```
|
||||
pip install scubagoggles
|
||||
```
|
||||
2. Next, run the following command. Depending on your system, you may
|
||||
also need to substitute `pip` with `pip3`.
|
||||
|
||||
```
|
||||
pip install scubagoggles
|
||||
```
|
||||
3. If you want to run the Configuration UI (user interface), use the following
|
||||
command instead of, or in addition to, the command above to install the
|
||||
additional packages required by the UI.
|
||||
|
||||
```
|
||||
pip install scubagoggles[ui]
|
||||
```
|
||||
|
||||
*NOTE: if you are installing ScubaGoggles using a downloaded "wheel" file,
|
||||
add the "[ui]" at the end of the file specification:
|
||||
`pip install scubagoggles-1.0.0-py3-none-any.whl[ui]`*
|
||||
|
||||
At this point, ScubaGoggles will be installed, but additional setup
|
||||
steps are necessary before it can be used. Continue to [Download the OPA executable](OPA.md)
|
||||
|
||||
@@ -30,6 +30,9 @@ license = "CC0-1.0"
|
||||
license-files = ["LICENSE"]
|
||||
readme = "README.md"
|
||||
|
||||
[project.optional-dependencies]
|
||||
ui = ["streamlit>=1.35.0"]
|
||||
|
||||
[project.scripts]
|
||||
scubagoggles = "scubagoggles.main:dive"
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ class TestMain:
|
||||
def fake_get_gws_args(parser, _user_config):
|
||||
parser.set_defaults(dispatch=make_dispatch("gws"))
|
||||
|
||||
def fake_get_gws_ui_args(parser, _user_config):
|
||||
def fake_get_ui_args(parser):
|
||||
parser.set_defaults(dispatch=make_dispatch("ui"))
|
||||
|
||||
def fake_get_opa_args(parser, _user_config):
|
||||
@@ -413,7 +413,7 @@ class TestMain:
|
||||
parser.set_defaults(dispatch=make_dispatch("version"))
|
||||
|
||||
monkeypatch.setattr(main_module, "get_gws_args", fake_get_gws_args)
|
||||
monkeypatch.setattr(main_module, "get_gws_ui_args", fake_get_gws_ui_args)
|
||||
monkeypatch.setattr(main_module, "get_ui_args", fake_get_ui_args)
|
||||
monkeypatch.setattr(main_module, "get_opa_args", fake_get_opa_args)
|
||||
monkeypatch.setattr(main_module, "get_purge_args", fake_get_purge_args)
|
||||
monkeypatch.setattr(main_module, "get_setup_args", fake_get_setup_args)
|
||||
|
||||
@@ -69,14 +69,8 @@ class TestLaunch:
|
||||
# avoid backslashes to bypass OS ambiguity Path to str conversion
|
||||
app_path = Path('home')
|
||||
command, port = launch._build_streamlit_command(app_path, force_dark=dark_mode)
|
||||
expected_cmd = [
|
||||
str(sys.executable), '-m', 'streamlit', 'run',
|
||||
'home',
|
||||
'--server.address', 'localhost',
|
||||
'--server.port', '1000',
|
||||
'--server.headless', 'false',
|
||||
'--browser.gatherUsageStats', 'false',
|
||||
]
|
||||
expected_cmd = self._streamlit_command('1000')
|
||||
|
||||
# mock return
|
||||
assert port == 1000
|
||||
if dark_mode:
|
||||
@@ -177,20 +171,13 @@ class TestLaunch:
|
||||
monkeypatch.delenv('SCUBAGOGGLES_UI_DARK', raising=False)
|
||||
|
||||
# expected build command
|
||||
expected_cmd = [
|
||||
str(sys.executable), '-m', 'streamlit', 'run',
|
||||
'home',
|
||||
'--server.address', 'localhost',
|
||||
'--server.port', '1234',
|
||||
'--server.headless', 'false',
|
||||
'--browser.gatherUsageStats', 'false',
|
||||
]
|
||||
expected_cmd = self._streamlit_command('1234')
|
||||
|
||||
if dark_mode:
|
||||
expected_cmd.extend(['--theme.base', 'dark'])
|
||||
# arbitrary return values for mocked functions
|
||||
mocker.patch.object(launch, '_find_free_port', return_value=1234)
|
||||
mocker.patch.object(launch, '_get_app_to_run', return_value=Path('home'))
|
||||
mocker.patch.object(launch, '_prevent_streamlit_promotion')
|
||||
real_print = builtins.print
|
||||
mock_print = mocker.patch('builtins.print', side_effect=real_print)
|
||||
mock_run_server = mocker.patch.object(launch, '_run_server')
|
||||
@@ -216,3 +203,21 @@ class TestLaunch:
|
||||
mock_print.assert_any_call('Opening http://localhost:1234 in your browser.')
|
||||
mock_print.assert_any_call('Press Ctrl+C to stop the server.\n')
|
||||
mock_run_server.assert_called_once_with(expected_cmd, popen_kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _streamlit_command(port: str) -> str:
|
||||
|
||||
"""Builds the list of command arguments for the expected streamlit
|
||||
command using the given port number.
|
||||
"""
|
||||
|
||||
command = [sys.executable, '-m', 'streamlit', 'run',
|
||||
'home',
|
||||
'--server.address', 'localhost',
|
||||
'--server.port', port,
|
||||
'--server.headless', 'false',
|
||||
'--browser.gatherUsageStats', 'false',
|
||||
'--server.showEmailPrompt', 'false',
|
||||
'--logger.hideWelcomeMessage', 'true']
|
||||
|
||||
return command
|
||||
@@ -21,7 +21,7 @@ from scubagoggles.scuba_argument_parser import ScubaArgumentParser
|
||||
from scubagoggles.user_setup import default_file_names, user_setup
|
||||
from scubagoggles.utils import path_parser
|
||||
from scubagoggles.version import Version
|
||||
from scubagoggles.ui import launch
|
||||
from scubagoggles.ui.launch import launch_from_ui_command as ui_launch
|
||||
from scubagoggles.scuba_constants import NUMBER_OF_UUID_CHARACTERS_TO_TRUNCATE_CHOICES, OPA_VERSION
|
||||
|
||||
EXIT_FAILURE = 1
|
||||
@@ -282,14 +282,14 @@ def get_gws_args(parser: argparse.ArgumentParser, user_config: UserConfig):
|
||||
action='store_true',
|
||||
help=help_msg)
|
||||
|
||||
def get_gws_ui_args(parser: argparse.ArgumentParser, user_config: UserConfig): # pylint: disable=unused-argument
|
||||
def get_ui_args(parser: argparse.ArgumentParser):
|
||||
"""Adds the arguments for the UI parser
|
||||
|
||||
:param argparse.ArgumentParser parser: argparse object
|
||||
:param UserConfig user_config: user configuration object
|
||||
"""
|
||||
|
||||
parser.set_defaults(dispatch=launch.launch_from_ui_command)
|
||||
parser.set_defaults(dispatch=ui_launch)
|
||||
|
||||
parser.add_argument('--darkmode',
|
||||
'-dm',
|
||||
@@ -492,7 +492,7 @@ def dive():
|
||||
gws_parser = subparsers.add_parser('ui',
|
||||
description=help_msg,
|
||||
help=help_msg)
|
||||
get_gws_ui_args(gws_parser, user_config)
|
||||
get_ui_args(gws_parser)
|
||||
|
||||
help_msg = 'Download OPA executable'
|
||||
getopa_parser = subparsers.add_parser('getopa',
|
||||
|
||||
@@ -60,8 +60,8 @@ The application consolidates all configuration capabilities into a single, profe
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
# Install all requirements including UI dependencies (from the ScubaGoggles directory)
|
||||
pip install -r requirements.txt
|
||||
# Install the additional UI dependencies
|
||||
pip install scubagoggles[ui]
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -70,7 +70,7 @@ pip install -r requirements.txt
|
||||
|
||||
```bash
|
||||
# From the ScubaGoggles root directory
|
||||
scubagoggles ui
|
||||
scubagoggles ui
|
||||
```
|
||||
|
||||
This will start the Streamlit server and open the app in your default web browser. Dark mode is automatically detected from your browser's preferred color scheme via CSS media queries. It can additionally be specified from the command line by running:
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
"""
|
||||
ScubaGoggles UI Package
|
||||
Configuration interface for ScubaGoggles using Streamlit
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "CISA SCuBA Team"
|
||||
|
||||
from .scubaconfigapp import ScubaConfigApp
|
||||
from .validation import ConfigValidator
|
||||
|
||||
__all__ = [
|
||||
"ScubaConfigApp",
|
||||
"ConfigValidator",
|
||||
]
|
||||
@@ -25,23 +25,6 @@ def _find_free_port() -> int:
|
||||
s.bind(("localhost", 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
def _prevent_streamlit_promotion() -> None:
|
||||
|
||||
"""Streamlit self-promotes upon initial invocation, which is not
|
||||
appropriate particularly for this application. It can be avoided by
|
||||
creating its "credentials" file with an empty email address. This is
|
||||
only done if the file doesn't already exist.
|
||||
"""
|
||||
|
||||
streamlit_dir = Path('~/.streamlit').expanduser()
|
||||
|
||||
streamlit_dir.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
streamlit_config = streamlit_dir / 'credentials.toml'
|
||||
|
||||
if not streamlit_config.exists():
|
||||
streamlit_config.write_text('[general]\nemail = ""\n',
|
||||
encoding='utf-8')
|
||||
|
||||
def _resolve_app_file() -> Path | None:
|
||||
"""Return the path to the Streamlit app file."""
|
||||
@@ -80,9 +63,9 @@ def _get_app_to_run() -> Path:
|
||||
print("Please ensure the UI modules are properly installed.")
|
||||
raise SystemExit(1)
|
||||
if not _is_streamlit_installed():
|
||||
print("Streamlit is not installed!")
|
||||
print("Please install requirements:")
|
||||
print(" pip install -r requirements.txt")
|
||||
print("Streamlit is required for the graphical user interface.")
|
||||
print("Please install using this command (see documentation):")
|
||||
print(" pip install scubagoggles[ui]")
|
||||
raise SystemExit(1)
|
||||
return app_to_run
|
||||
|
||||
@@ -101,6 +84,8 @@ def _build_streamlit_command(app_to_run: Path,
|
||||
"--server.port", str(port),
|
||||
"--server.headless", "false",
|
||||
"--browser.gatherUsageStats", "false",
|
||||
"--server.showEmailPrompt", "false",
|
||||
"--logger.hideWelcomeMessage", "true"
|
||||
]
|
||||
# add extra option if dark option specified on command line
|
||||
if force_dark:
|
||||
@@ -143,9 +128,6 @@ def launch_main(darkmode = False) -> None:
|
||||
|
||||
cmd, port = _build_streamlit_command(app_to_run, force_dark)
|
||||
|
||||
|
||||
_prevent_streamlit_promotion()
|
||||
|
||||
popen_kwargs: dict = {}
|
||||
if sys.platform != "win32":
|
||||
# On Unix, start a new session so os.killpg can target the group.
|
||||
|
||||
Reference in new issue
Block a user