Skip to content

Commit d427fae

Browse files
authored
Add --no-browser option (#611)
* Add no_browser option to disable automatic browser opening * Improve help text * Update README.md
1 parent f2a5168 commit d427fae

7 files changed

Lines changed: 41 additions & 21 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ Options:
509509
--push Push local modifications to the cloud before starting live trading
510510
--open Automatically open the live results in the browser once the deployment starts
511511
--show-secrets Show secrets as they are input
512+
--no-browser Display OAuth URL without opening the browser
512513
--verbose Enable debug logging
513514
--help Show this message and exit.
514515
```
@@ -1507,6 +1508,7 @@ Options:
15071508
--extra-docker-config TEXT Extra docker configuration as a JSON string. For more information https://docker-
15081509
py.readthedocs.io/en/stable/containers.html
15091510
--no-update Use the local LEAN engine image instead of pulling the latest version
1511+
--no-browser Display OAuth URL without opening the browser
15101512
--lean-config FILE The Lean configuration file that should be used (defaults to the nearest lean.json)
15111513
--verbose Enable debug logging
15121514
--help Show this message and exit.

lean/commands/cloud/live/deploy.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ def _configure_auto_restart(logger: Logger) -> bool:
202202
default=False,
203203
help="Automatically open the live results in the browser once the deployment starts")
204204
@option("--show-secrets", is_flag=True, show_default=True, default=False, help="Show secrets as they are input")
205+
@option("--no-browser",
206+
is_flag=True,
207+
default=False,
208+
help="Display OAuth URL without opening the browser")
205209
def deploy(project: str,
206210
brokerage: str,
207211
data_provider_live: Optional[str],
@@ -218,6 +222,7 @@ def deploy(project: str,
218222
push: bool,
219223
open_browser: bool,
220224
show_secrets: bool,
225+
no_browser: bool,
221226
**kwargs) -> None:
222227
"""Start live trading for a project in the cloud.
223228
@@ -246,7 +251,7 @@ def deploy(project: str,
246251
ensure_options(["brokerage", "node", "auto_restart", "notify_order_events", "notify_insights"])
247252

248253
brokerage_instance = non_interactive_config_build_for_name(lean_config, brokerage, cloud_brokerages,
249-
kwargs, logger)
254+
kwargs, logger, no_browser=no_browser)
250255
notify_methods = []
251256
if notify_emails is not None:
252257
for config in notify_emails.split(","):
@@ -288,7 +293,7 @@ def deploy(project: str,
288293
else:
289294
# let the user choose the brokerage
290295
brokerage_instance = interactive_config_build(lean_config, cloud_brokerages, logger, kwargs, show_secrets,
291-
"Select a brokerage", multiple=False)
296+
"Select a brokerage", multiple=False, no_browser=no_browser)
292297

293298
notify_order_events, notify_insights, notify_methods = _configure_notifications(logger)
294299
auto_restart = _configure_auto_restart(logger)
@@ -304,13 +309,13 @@ def deploy(project: str,
304309
# the user sent the live data provider to use
305310
for data_provider in data_provider_live:
306311
data_provider_instance = non_interactive_config_build_for_name(lean_config, data_provider,
307-
cloud_data_queue_handlers, kwargs, logger)
312+
cloud_data_queue_handlers, kwargs, logger, no_browser=no_browser)
308313

309314
live_data_provider_settings.update({data_provider_instance.get_id(): data_provider_instance.get_settings()})
310315
else:
311316
# let's ask the user which live data providers to use
312317
data_feed_instances = interactive_config_build(lean_config, cloud_data_queue_handlers, logger, kwargs,
313-
show_secrets, "Select a live data feed", multiple=True)
318+
show_secrets, "Select a live data feed", multiple=True, no_browser=no_browser)
314319
for data_feed in data_feed_instances:
315320
settings = data_feed.get_settings()
316321

@@ -354,7 +359,6 @@ def deploy(project: str,
354359
live_holdings)
355360

356361
logger.info(f"Live url: {live_algorithm.get_url()}")
357-
358362
if open_browser:
359363
from webbrowser import open
360364
open(live_algorithm.get_url())

lean/commands/live/deploy.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ def _get_history_provider_name(data_provider_live_names: [str]) -> [str]:
114114
is_flag=True,
115115
default=False,
116116
help="Use the local LEAN engine image instead of pulling the latest version")
117+
@option("--no-browser",
118+
is_flag=True,
119+
default=False,
120+
help="Display OAuth URL without opening the browser")
117121
def deploy(project: Path,
118122
environment: Optional[str],
119123
output: Optional[Path],
@@ -132,6 +136,7 @@ def deploy(project: Path,
132136
extra_config: Optional[Tuple[str, str]],
133137
extra_docker_config: Optional[str],
134138
no_update: bool,
139+
no_browser: bool,
135140
**kwargs) -> None:
136141
"""Start live trading a project locally using Docker.
137142
@@ -206,30 +211,30 @@ def deploy(project: Path,
206211
if brokerage:
207212
# user provided brokerage, check all arguments were provided
208213
brokerage_instance = non_interactive_config_build_for_name(lean_config, brokerage, cli_brokerages, kwargs,
209-
logger, environment_name)
214+
logger, environment_name, no_browser=no_browser)
210215
else:
211216
# let the user choose the brokerage
212217
brokerage_instance = interactive_config_build(lean_config, cli_brokerages, logger, kwargs, show_secrets,
213218
"Select a brokerage", multiple=False,
214-
environment_name=environment_name)
219+
environment_name=environment_name, no_browser=no_browser)
215220

216221
if data_provider_live and len(data_provider_live) > 0:
217222
for data_feed_name in data_provider_live:
218223
data_feed = non_interactive_config_build_for_name(lean_config, data_feed_name, cli_data_queue_handlers,
219-
kwargs, logger, environment_name)
224+
kwargs, logger, environment_name, no_browser=no_browser)
220225
data_provider_live_instances.append(data_feed)
221226
else:
222227
data_provider_live_instances = interactive_config_build(lean_config, cli_data_queue_handlers, logger, kwargs,
223228
show_secrets, "Select a live data feed", multiple=True,
224-
environment_name=environment_name)
229+
environment_name=environment_name, no_browser=no_browser)
225230

226231
# based on the live data providers we set up the history providers
227232
data_provider_live = [provider.get_name() for provider in data_provider_live_instances]
228233
if data_provider_historical is None:
229234
data_provider_historical = "Local"
230235
data_downloader_instances = non_interactive_config_build_for_name(lean_config, data_provider_historical,
231236
cli_data_downloaders, kwargs, logger,
232-
environment_name)
237+
environment_name, no_browser=no_browser)
233238
if history_providers is None or len(history_providers) == 0:
234239
history_providers = _get_history_provider_name(data_provider_live)
235240
for history_provider in history_providers:

lean/components/api/auth0_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,24 @@ def read(self, brokerage_id: str) -> QCAuth0Authorization:
5252
return QCAuth0Authorization(authorization=None)
5353

5454
@staticmethod
55-
def authorize(brokerage_id: str, logger: Logger, project_id: int) -> None:
55+
def authorize(brokerage_id: str, logger: Logger, project_id: int, no_browser: bool = False) -> None:
5656
"""Starts the authorization process for a brokerage.
5757
5858
:param brokerage_id: the id of the brokerage to start the authorization process for
5959
:param logger: the logger instance to use
6060
:param project_id: The local or cloud project_id
61+
:param no_browser: whether to disable opening the browser
6162
"""
6263
from webbrowser import open
6364

6465
full_url = f"{API_BASE_URL}live/auth0/authorize?brokerage={brokerage_id}&projectId={project_id}"
6566

6667
logger.info(f"Please open the following URL in your browser to authorize the LEAN CLI.")
6768
logger.info(full_url)
69+
70+
if no_browser:
71+
return
72+
6873
open(full_url)
6974

7075

lean/components/util/auth0_helper.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
from lean.components.util.logger import Logger
1717

1818

19-
def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logger, project_id: int) -> QCAuth0Authorization:
19+
def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logger, project_id: int, no_browser: bool = False) -> QCAuth0Authorization:
2020
"""Gets the authorization data for a brokerage, authorizing if necessary.
2121
2222
:param auth0_client: An instance of Auth0Client, containing methods to interact with live/auth0/* API endpoints.
2323
:param brokerage_id: The ID of the brokerage to get the authorization data for.
2424
:param logger: An instance of Logger, handling all output printing.
2525
:param project_id: The local or cloud project_id.
26+
:param no_browser: whether to disable opening the browser
2627
:return: The authorization data for the specified brokerage.
2728
"""
2829
from time import time, sleep
@@ -32,7 +33,7 @@ def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logg
3233
return data
3334

3435
start_time = time()
35-
auth0_client.authorize(brokerage_id, logger, project_id)
36+
auth0_client.authorize(brokerage_id, logger, project_id, no_browser)
3637

3738
# keep checking for new data every 5 seconds for 7 minutes
3839
while time() - start_time < 420:

lean/components/util/json_modules_handler.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ def build_and_configure_modules(target_modules: List[str], module_list: List[Jso
4141

4242
def non_interactive_config_build_for_name(lean_config: Dict[str, Any], target_module_name: str,
4343
module_list: List[JsonModule], properties: Dict[str, Any], logger: Logger,
44-
environment_name: str = None) -> JsonModule:
44+
environment_name: str = None, no_browser: bool = False) -> JsonModule:
4545
return config_build_for_name(lean_config, target_module_name, module_list, properties, logger, interactive=False,
46-
environment_name=environment_name)
46+
environment_name=environment_name, no_browser=no_browser)
4747

4848

4949
def find_module(target_module_name: str, module_list: List[JsonModule], logger: Logger) -> JsonModule:
@@ -79,17 +79,17 @@ def find_module(target_module_name: str, module_list: List[JsonModule], logger:
7979

8080
def config_build_for_name(lean_config: Dict[str, Any], target_module_name: str, module_list: List[JsonModule],
8181
properties: Dict[str, Any], logger: Logger, interactive: bool,
82-
environment_name: str = None) -> JsonModule:
82+
environment_name: str = None, no_browser: bool = False) -> JsonModule:
8383
target_module = find_module(target_module_name, module_list, logger)
8484
target_module.config_build(lean_config, logger, interactive=interactive, properties=properties,
85-
environment_name=environment_name)
85+
environment_name=environment_name, no_browser=no_browser)
8686
_update_settings(logger, environment_name, target_module, lean_config)
8787
return target_module
8888

8989

9090
def interactive_config_build(lean_config: Dict[str, Any], models: [JsonModule], logger: Logger,
9191
user_provided_options: Dict[str, Any], show_secrets: bool, select_message: str,
92-
multiple: bool, environment_name: str = None) -> [JsonModule]:
92+
multiple: bool, environment_name: str = None, no_browser: bool = False) -> [JsonModule]:
9393
"""Interactively configures the brokerage to use.
9494
9595
:param lean_config: the LEAN configuration that should be used
@@ -100,6 +100,7 @@ def interactive_config_build(lean_config: Dict[str, Any], models: [JsonModule],
100100
:param select_message: the user facing selection message
101101
:param multiple: true if multiple selections are allowed
102102
:param environment_name: the target environment name
103+
:param no_browser: whether to disable opening the browser
103104
:return: the brokerage the user configured
104105
"""
105106
options = [Option(id=b, label=b.get_name()) for b in models]
@@ -113,7 +114,7 @@ def interactive_config_build(lean_config: Dict[str, Any], models: [JsonModule],
113114

114115
for module in modules:
115116
module.config_build(lean_config, logger, interactive=True, properties=user_provided_options,
116-
hide_input=not show_secrets, environment_name=environment_name)
117+
hide_input=not show_secrets, environment_name=environment_name, no_browser=no_browser)
117118
_update_settings(logger, environment_name, module, lean_config)
118119
if multiple:
119120
return modules

lean/models/json_module.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ def config_build(self,
195195
interactive: bool,
196196
properties: Dict[str, Any] = {},
197197
hide_input: bool = False,
198-
environment_name: str = None) -> 'JsonModule':
198+
environment_name: str = None,
199+
no_browser: bool = False) -> 'JsonModule':
199200
"""Builds a new instance of this class, prompting the user for input when necessary.
200201
201202
:param lean_config: the Lean configuration dict to read defaults from
@@ -204,6 +205,7 @@ def config_build(self,
204205
:param properties: the properties that passed as options
205206
:param hide_input: whether to hide secrets inputs
206207
:param environment_name: the target environment name
208+
:param no_browser: whether to disable opening the browser
207209
:return: self
208210
"""
209211
logger.debug(f'Configuring {self._display_name}')
@@ -237,7 +239,7 @@ def config_build(self,
237239
configuration.require_project_id)
238240
logger.debug(f'project_id: {lean_config["project-id"]}')
239241
auth_authorizations = get_authorization(container.api_client.auth0, self._display_name.lower(),
240-
logger, lean_config["project-id"])
242+
logger, lean_config["project-id"], no_browser=no_browser)
241243
logger.debug(f'auth: {auth_authorizations}')
242244
configuration._value = auth_authorizations.get_authorization_config_without_account()
243245
for inner_config in self._lean_configs:

0 commit comments

Comments
 (0)