From 67d3ce7f3402518d3a81a7a82ee7db9f765adac8 Mon Sep 17 00:00:00 2001 From: Romazes Date: Fri, 13 Mar 2026 17:19:49 +0200 Subject: [PATCH 1/3] feat: add --charles-schwab-user-name option to README Regenerates README to document the new --charles-schwab-user-name CLI option for Charles Schwab OAuth authentication across all relevant commands. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 9761cf48..0fab3c26 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,8 @@ Options: --kraken-api-secret TEXT Your Kraken API secret --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier + --charles-schwab-user-name TEXT + Your Charles Schwab username used for OAuth authentication --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary @@ -461,6 +463,8 @@ Options: --kraken-api-secret TEXT Your Kraken API secret --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier + --charles-schwab-user-name TEXT + Your Charles Schwab username used for OAuth authentication --charles-schwab-account-number TEXT The CharlesSchwab account number --bybit-api-key TEXT Your Bybit API key @@ -927,6 +931,8 @@ Options: --kraken-api-secret TEXT Your Kraken API secret --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier + --charles-schwab-user-name TEXT + Your Charles Schwab username used for OAuth authentication --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary @@ -1444,6 +1450,8 @@ Options: --kraken-api-secret TEXT Your Kraken API secret --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier + --charles-schwab-user-name TEXT + Your Charles Schwab username used for OAuth authentication --charles-schwab-account-number TEXT The CharlesSchwab account number --bybit-api-key TEXT Your Bybit API key @@ -1850,6 +1858,8 @@ Options: --kraken-api-secret TEXT Your Kraken API secret --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier + --charles-schwab-user-name TEXT + Your Charles Schwab username used for OAuth authentication --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary @@ -2108,6 +2118,8 @@ Options: --kraken-api-secret TEXT Your Kraken API secret --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier + --charles-schwab-user-name TEXT + Your Charles Schwab username used for OAuth authentication --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary From 0f70d6f2ed2c11cf864d9c350aa2ffcf74bf7778 Mon Sep 17 00:00:00 2001 From: Romazes Date: Fri, 13 Mar 2026 17:28:15 +0200 Subject: [PATCH 2/3] refactor: help text to new charles-schwab-user-name parameter --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0fab3c26..1d303260 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Options: --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier --charles-schwab-user-name TEXT - Your Charles Schwab username used for OAuth authentication + Your Charles Schwab login ID --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary @@ -464,7 +464,7 @@ Options: --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier --charles-schwab-user-name TEXT - Your Charles Schwab username used for OAuth authentication + Your Charles Schwab login ID --charles-schwab-account-number TEXT The CharlesSchwab account number --bybit-api-key TEXT Your Bybit API key @@ -932,7 +932,7 @@ Options: --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier --charles-schwab-user-name TEXT - Your Charles Schwab username used for OAuth authentication + Your Charles Schwab login ID --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary @@ -1451,7 +1451,7 @@ Options: --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier --charles-schwab-user-name TEXT - Your Charles Schwab username used for OAuth authentication + Your Charles Schwab login ID --charles-schwab-account-number TEXT The CharlesSchwab account number --bybit-api-key TEXT Your Bybit API key @@ -1859,7 +1859,7 @@ Options: --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier --charles-schwab-user-name TEXT - Your Charles Schwab username used for OAuth authentication + Your Charles Schwab login ID --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary @@ -2119,7 +2119,7 @@ Options: --kraken-verification-tier [Starter|Intermediate|Pro] Your Kraken Verification Tier --charles-schwab-user-name TEXT - Your Charles Schwab username used for OAuth authentication + Your Charles Schwab login ID --charles-schwab-account-number TEXT The CharlesSchwab account number --iqfeed-iqconnect TEXT The path to the IQConnect binary From 4ebf21015ddceb7f98df98221ef67b34fb6a0fad Mon Sep 17 00:00:00 2001 From: Romazes Date: Mon, 16 Mar 2026 18:58:15 +0200 Subject: [PATCH 3/3] feat: add interactive param to get_user_name, raise when non-interactive and user name missing --- lean/models/json_module.py | 8 ++++++-- tests/commands/test_live.py | 3 ++- tests/components/util/test_json_modules_handler.py | 9 +++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lean/models/json_module.py b/lean/models/json_module.py index 3f83538a..19411dba 100644 --- a/lean/models/json_module.py +++ b/lean/models/json_module.py @@ -175,13 +175,15 @@ def convert_variable_to_lean_key(self, variable_key: str) -> str: """ return variable_key.replace('_', '-') - def get_user_name(self, lean_config: Dict[str, Any], configuration, user_provided_options: Dict[str, Any], require_user_name: bool) -> str: + def get_user_name(self, lean_config: Dict[str, Any], configuration, user_provided_options: Dict[str, Any], + require_user_name: bool, interactive: bool) -> str: """Retrieve the user name, prompting the user if required and not already set. :param lean_config: The Lean config dict to read defaults from. :param configuration: The AuthConfiguration instance. :param user_provided_options: Options passed as command-line arguments. :param require_user_name: Flag to determine if prompting is necessary. + :param interactive: ask user input parameter manually (interactively) :return: The user name, or None if not required. """ if not require_user_name: @@ -193,6 +195,8 @@ def get_user_name(self, lean_config: Dict[str, Any], configuration, user_provide return user_provided_options[user_name_variable] if lean_config and lean_config.get(user_name_key): return lean_config[user_name_key] + if not interactive: + raise RuntimeError(f"You are missing the following option {user_name_key}") user_name = prompt("Please enter your Login ID to proceed with Auth0 authentication", show_default=False) if lean_config is not None: @@ -263,7 +267,7 @@ def config_build(self, configuration.require_project_id) logger.debug(f'project_id: {lean_config["project-id"]}') user_name = self.get_user_name(lean_config, configuration, user_provided_options, - configuration.require_user_name) + configuration.require_user_name, interactive) logger.debug(f'user_name: {user_name}') auth_authorizations = get_authorization(container.api_client.auth0, self._display_name.lower(), logger, lean_config["project-id"], no_browser=no_browser, diff --git a/tests/commands/test_live.py b/tests/commands/test_live.py index efe9d66d..6a4cbc3f 100644 --- a/tests/commands/test_live.py +++ b/tests/commands/test_live.py @@ -432,7 +432,8 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme "tt-log-fix-messages": "no" }, "CharlesSchwab": { - "charles-schwab-account-number": "123" + "charles-schwab-account-number": "123", + "charles-schwab-user-name": "user" }, "Bybit": { "bybit-api-key": "abc", diff --git a/tests/components/util/test_json_modules_handler.py b/tests/components/util/test_json_modules_handler.py index bb46eb8e..2113be8d 100644 --- a/tests/components/util/test_json_modules_handler.py +++ b/tests/components/util/test_json_modules_handler.py @@ -81,7 +81,8 @@ def test_is_value_in_config(searching: str, expected: bool) -> None: def test_get_user_name_returns_none_when_not_required() -> None: module = JsonModule({"id": "test", "configurations": [], "display-id": "Test"}, MODULE_BROKERAGE, MODULE_CLI_PLATFORM) - result = module.get_user_name({}, mock.Mock(), {}, require_user_name=False) + result = module.get_user_name({}, mock.Mock(), {}, require_user_name=False, + interactive=False) assert result is None @@ -92,7 +93,7 @@ def test_get_user_name_from_user_provided_options() -> None: config._id = "charles-schwab-oauth-token" result = module.get_user_name({}, config, {"charles_schwab_user_name": "cli_login"}, - require_user_name=True) + require_user_name=True, interactive=False) assert result == "cli_login" @@ -102,7 +103,7 @@ def test_get_user_name_from_lean_config() -> None: config = mock.Mock() config._id = "charles-schwab-oauth-token" lean_config = {"charles-schwab-user-name": "saved_login"} - result = module.get_user_name(lean_config, config, {}, require_user_name=True) + result = module.get_user_name(lean_config, config, {}, require_user_name=True, interactive=False) assert result == "saved_login" @@ -113,7 +114,7 @@ def test_get_user_name_prompts_and_saves_to_lean_config() -> None: config._id = "charles-schwab-oauth-token" lean_config = {} with mock.patch("click.prompt", return_value="prompted_login") as mock_prompt: - result = module.get_user_name(lean_config, config, {}, require_user_name=True) + result = module.get_user_name(lean_config, config, {}, require_user_name=True, interactive=True) assert result == "prompted_login" assert lean_config["charles-schwab-user-name"] == "prompted_login" mock_prompt.assert_called_once()