Skip to content

Commit 80d3eca

Browse files
committed
Fix choice casing in CLI help text and README after Click 8+ upgrade
1 parent 5e64f59 commit 80d3eca

14 files changed

Lines changed: 134 additions & 119 deletions

File tree

README.md

Lines changed: 70 additions & 70 deletions
Large diffs are not rendered by default.

lean/click.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pathlib import Path
1515
from typing import Optional, List, Callable
1616

17-
from click import Command, Context, Parameter, ParamType, Option as ClickOption
17+
from click import Choice, Command, Context, Parameter, ParamType, Option as ClickOption
1818
from click.decorators import FC, option
1919

2020
from lean.constants import DEFAULT_LEAN_CONFIG_FILE_NAME, CONTAINER_LABEL_LEAN_VERSION_NAME
@@ -305,6 +305,20 @@ def _parse_config_option(self, ctx: Context, param: Parameter, value: Optional[P
305305

306306

307307

308+
class CaseInsensitiveChoice(Choice):
309+
"""A click.Choice subclass that matches case-insensitively but displays original casing in help text."""
310+
311+
def __init__(self, choices, **kwargs):
312+
super().__init__(choices, case_sensitive=False, **kwargs)
313+
314+
def get_metavar(self, param, ctx=None) -> str:
315+
import enum
316+
choices_str = "|".join(c.value if isinstance(c, enum.Enum) else str(c) for c in self.choices)
317+
if param is not None and param.required and param.param_type_name == "argument":
318+
return f"{{{choices_str}}}"
319+
return f"[{choices_str}]"
320+
321+
308322
class PathParameter(ParamType):
309323
"""A limited version of click.Path which uses pathlib.Path."""
310324

lean/commands/backtest.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
from pathlib import Path
1515
from typing import List, Optional, Tuple
16-
from click import command, option, argument, Choice
16+
from click import command, option, argument
1717

18-
from lean.click import LeanCommand, PathParameter, backtest_parameter_option
18+
from lean.click import LeanCommand, PathParameter, backtest_parameter_option, CaseInsensitiveChoice
1919
from lean.constants import DEFAULT_ENGINE_IMAGE, LEAN_ROOT_PATH
2020
from lean.container import container, Logger
2121
from lean.models.utils import DebuggingMethod
@@ -234,10 +234,10 @@ def _migrate_csharp_csproj(project_dir: Path) -> None:
234234
default=False,
235235
help="Run the backtest in a detached Docker container and return immediately")
236236
@option("--debug",
237-
type=Choice(["pycharm", "ptvsd", "debugpy", "vsdbg", "rider", "local-platform"], case_sensitive=False),
237+
type=CaseInsensitiveChoice(["pycharm", "ptvsd", "debugpy", "vsdbg", "rider", "local-platform"]),
238238
help="Enable a certain debugging method (see --help for more information)")
239239
@option("--data-provider-historical",
240-
type=Choice([dp.get_name() for dp in cli_data_downloaders], case_sensitive=False),
240+
type=CaseInsensitiveChoice([dp.get_name() for dp in cli_data_downloaders]),
241241
default="Local",
242242
help="Update the Lean configuration file to retrieve data from the given historical provider")
243243
@options_from_json(get_configs_for_options("backtest"))

lean/commands/cloud/live/deploy.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
# limitations under the License.
1313

1414
from typing import List, Tuple, Optional
15-
from click import prompt, option, argument, Choice, confirm
16-
from lean.click import LeanCommand, ensure_options
15+
from click import prompt, option, argument, confirm
16+
from lean.click import LeanCommand, ensure_options, CaseInsensitiveChoice
1717
from lean.components.api.api_client import APIClient
1818
from lean.components.util.json_modules_handler import non_interactive_config_build_for_name, \
1919
interactive_config_build
@@ -166,10 +166,10 @@ def _configure_auto_restart(logger: Logger) -> bool:
166166
@live.command(cls=LeanCommand, default_command=True, name="deploy")
167167
@argument("project", type=str)
168168
@option("--brokerage",
169-
type=Choice([b.get_name() for b in cloud_brokerages], case_sensitive=False),
169+
type=CaseInsensitiveChoice([b.get_name() for b in cloud_brokerages]),
170170
help="The brokerage to use")
171171
@option("--data-provider-live",
172-
type=Choice([d.get_name() for d in cloud_data_queue_handlers], case_sensitive=False),
172+
type=CaseInsensitiveChoice([d.get_name() for d in cloud_data_queue_handlers]),
173173
multiple=True,
174174
help="The live data provider to use")
175175
@options_from_json(get_configs_for_options("live-cloud"))

lean/commands/cloud/optimize.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
from typing import List, Optional, Tuple
1515

16-
from click import command, option, Choice, argument, confirm
16+
from click import command, option, argument, confirm
1717

18-
from lean.click import LeanCommand, ensure_options
18+
from lean.click import LeanCommand, ensure_options, CaseInsensitiveChoice
1919
from lean.components.config.optimizer_config_manager import NodeType, available_nodes
2020
from lean.container import container
2121
from lean.models.api import QCOptimizationBacktest, QCProject, QCCompileWithLogs, QCFullOrganization
@@ -156,7 +156,7 @@ def _display_estimate(cloud_project: QCProject,
156156
type=str,
157157
help="The target statistic of the optimization")
158158
@option("--target-direction",
159-
type=Choice(["min", "max"], case_sensitive=False),
159+
type=CaseInsensitiveChoice(["min", "max"]),
160160
help="Whether the target must be minimized or maximized")
161161
@option("--parameter",
162162
type=(str, float, float, float),
@@ -167,7 +167,7 @@ def _display_estimate(cloud_project: QCProject,
167167
multiple=True,
168168
help="The 'statistic operator value' pairs configuring the constraints of the optimization")
169169
@option("--node",
170-
type=Choice([node.name for node in available_nodes], case_sensitive=False),
170+
type=CaseInsensitiveChoice([node.name for node in available_nodes]),
171171
help="The node type to run the optimization on")
172172
@option("--parallel-nodes",
173173
type=int,

lean/commands/create_project.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
# limitations under the License.
1313

1414
from pathlib import Path
15-
from click import Choice, option, argument
15+
from click import option, argument
1616

17-
from lean.click import LeanCommand
17+
from lean.click import LeanCommand, CaseInsensitiveChoice
1818
from lean.commands import lean
1919
from lean.container import container
2020
from lean.models.api import QCLanguage
@@ -398,7 +398,7 @@ def _not_identifier_char(text):
398398
@lean.command(cls=LeanCommand, name="project-create", aliases=["create-project"])
399399
@argument("name", type=str)
400400
@option("--language", "-l",
401-
type=Choice(container.cli_config_manager.default_language.allowed_values, case_sensitive=False),
401+
type=CaseInsensitiveChoice(container.cli_config_manager.default_language.allowed_values),
402402
help="The language of the project to create")
403403
def create_project(name: str, language: str) -> None:
404404
"""Create a new project containing starter code.

lean/commands/data/download.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
from docker.types import Mount
1717
from typing import Any, Dict, Iterable, List, Optional
18-
from click import command, option, confirm, pass_context, Context, Choice, prompt
19-
from lean.click import LeanCommand, ensure_options
18+
from click import command, option, confirm, pass_context, Context, prompt
19+
from lean.click import LeanCommand, ensure_options, CaseInsensitiveChoice
2020
from lean.components.util.json_modules_handler import config_build_for_name
2121
from lean.constants import DEFAULT_ENGINE_IMAGE
2222
from lean.container import container
@@ -500,7 +500,7 @@ def _configure_date_option(date_value: str, option_id: str, option_label: str) -
500500
return date_option.configure_non_interactive(date_value)
501501

502502

503-
class QCDataTypeCustomChoice(Choice):
503+
class QCDataTypeCustomChoice(CaseInsensitiveChoice):
504504
def get_metavar(self, param, ctx=None) -> str:
505505
choices_str = "|".join(QCDataType.get_all_members_except('Open Interest'))
506506

@@ -521,7 +521,7 @@ def _replace_data_type(ctx, param, value):
521521

522522
@command(cls=LeanCommand, requires_lean_config=True, allow_unknown_options=True, name="download")
523523
@option("--data-provider-historical",
524-
type=Choice([data_downloader.get_name() for data_downloader in cli_data_downloaders], case_sensitive=False),
524+
type=CaseInsensitiveChoice([data_downloader.get_name() for data_downloader in cli_data_downloaders]),
525525
help="The name of the downloader data provider.")
526526
@options_from_json(get_configs_for_options("download"))
527527
@option("--dataset", type=str, help="The name of the dataset to download non-interactively")
@@ -530,11 +530,11 @@ def _replace_data_type(ctx, param, value):
530530
@option("--yes", "-y", "auto_confirm", is_flag=True, default=False,
531531
help="Automatically confirm payment confirmation prompts")
532532
@option("--data-type", callback=_replace_data_type,
533-
type=QCDataTypeCustomChoice(QCDataType.get_all_members(), case_sensitive=False),
533+
type=QCDataTypeCustomChoice(QCDataType.get_all_members()),
534534
help="Specify the type of historical data")
535-
@option("--resolution", type=Choice(QCResolution.get_all_members(), case_sensitive=False),
535+
@option("--resolution", type=CaseInsensitiveChoice(QCResolution.get_all_members()),
536536
help="Specify the resolution of the historical data")
537-
@option("--security-type", type=Choice(QCSecurityType.get_all_members(), case_sensitive=False),
537+
@option("--security-type", type=CaseInsensitiveChoice(QCSecurityType.get_all_members()),
538538
help="Specify the security type of the historical data")
539539
@option("--market", type=str,
540540
help="Specify the market name for tickers (e.g., 'USA', 'NYMEX', 'Binance')"

lean/commands/data/generate.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
from datetime import datetime
1515
from typing import Optional
1616

17-
from click import command, option, Choice, IntRange
17+
from click import command, option, IntRange
1818

19-
from lean.click import DateParameter, LeanCommand, ensure_options
19+
from lean.click import DateParameter, LeanCommand, ensure_options, CaseInsensitiveChoice
2020
from lean.constants import DEFAULT_ENGINE_IMAGE
2121
from lean.container import container
2222

@@ -39,15 +39,15 @@
3939
default="",
4040
help="Comma separated list of tickers to use for generated data")
4141
@option("--security-type",
42-
type=Choice(["Equity", "Forex", "Cfd", "Future", "Crypto", "Option"], case_sensitive=False),
42+
type=CaseInsensitiveChoice(["Equity", "Forex", "Cfd", "Future", "Crypto", "Option"]),
4343
default="Equity",
4444
help="The security type to generate data for (defaults to Equity)")
4545
@option("--resolution",
46-
type=Choice(["Tick", "Second", "Minute", "Hour", "Daily"], case_sensitive=False),
46+
type=CaseInsensitiveChoice(["Tick", "Second", "Minute", "Hour", "Daily"]),
4747
default="Minute",
4848
help="The resolution of the generated data (defaults to Minute)")
4949
@option("--data-density",
50-
type=Choice(["Dense", "Sparse", "VerySparse"], case_sensitive=False),
50+
type=CaseInsensitiveChoice(["Dense", "Sparse", "VerySparse"]),
5151
default="Dense",
5252
help="The density of the generated data (defaults to Dense)")
5353
@option("--include-coarse",
@@ -98,7 +98,7 @@
9898
help="The stochastic process, and returns new pricing engine to run calculations for that option "
9999
"(defaults to BaroneAdesiWhaleyApproximationEngine)")
100100
@option("--volatility-model-resolution",
101-
type=Choice(["Tick", "Second", "Minute", "Hour", "Daily"], case_sensitive=False),
101+
type=CaseInsensitiveChoice(["Tick", "Second", "Minute", "Hour", "Daily"]),
102102
default="Daily",
103103
help="The volatility model period span (defaults to Daily)")
104104
@option("--chain-symbol-count",

lean/commands/init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from click import command, option, Choice, confirm, prompt
1818

19-
from lean.click import LeanCommand
19+
from lean.click import LeanCommand, CaseInsensitiveChoice
2020
from lean.commands.login import get_credentials, validate_credentials, get_lean_config_credentials
2121
from lean.constants import DEFAULT_DATA_DIRECTORY_NAME, DEFAULT_LEAN_CONFIG_FILE_NAME
2222
from lean.container import container
@@ -121,7 +121,7 @@ def _download_repository(output_path: Path) -> None:
121121
@command(cls=LeanCommand)
122122
@option("--organization", type=str, help="The name or id of the organization the Lean CLI will be scaffolded for")
123123
@option("--language", "-l",
124-
type=Choice(container.cli_config_manager.default_language.allowed_values, case_sensitive=False),
124+
type=CaseInsensitiveChoice(container.cli_config_manager.default_language.allowed_values),
125125
help="The default language to use for new projects")
126126
def init(organization: Optional[str], language: Optional[str]) -> None:
127127
"""Scaffold a Lean configuration file and data directory."""

lean/commands/live/deploy.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
from pathlib import Path
1515
from typing import List, Optional, Tuple
16-
from click import option, argument, Choice
17-
from lean.click import LeanCommand, PathParameter
16+
from click import option, argument
17+
from lean.click import LeanCommand, PathParameter, CaseInsensitiveChoice
1818
from lean.components.util.name_rename import rename_internal_config_to_user_friendly_format
1919
from lean.constants import DEFAULT_ENGINE_IMAGE
2020
from lean.container import container
@@ -64,14 +64,14 @@ def _get_history_provider_name(data_provider_live_names: [str]) -> [str]:
6464
default=False,
6565
help="Run the live deployment in a detached Docker container and return immediately")
6666
@option("--brokerage",
67-
type=Choice([b.get_name() for b in cli_brokerages], case_sensitive=False),
67+
type=CaseInsensitiveChoice([b.get_name() for b in cli_brokerages]),
6868
help="The brokerage to use")
6969
@option("--data-provider-live",
70-
type=Choice([d.get_name() for d in cli_data_queue_handlers], case_sensitive=False),
70+
type=CaseInsensitiveChoice([d.get_name() for d in cli_data_queue_handlers]),
7171
multiple=True,
7272
help="The live data provider to use")
7373
@option("--data-provider-historical",
74-
type=Choice([dp.get_name() for dp in cli_data_downloaders if dp.get_id() != "TerminalLinkBrokerage"], case_sensitive=False),
74+
type=CaseInsensitiveChoice([dp.get_name() for dp in cli_data_downloaders if dp.get_id() != "TerminalLinkBrokerage"]),
7575
help="Update the Lean configuration file to retrieve data from the given historical provider")
7676
@options_from_json(get_configs_for_options("live-cli"))
7777
@option("--release",

0 commit comments

Comments
 (0)