Skip to content

Commit

Permalink
Merge pull request #129 from rjra2611/update-and-cleanup
Browse files Browse the repository at this point in the history
Cleanup update error logs and broken links
  • Loading branch information
Martin-Molinero authored Aug 17, 2022
2 parents 34da965 + 8aa435d commit 2a2c927
Show file tree
Hide file tree
Showing 14 changed files with 62 additions and 32 deletions.
7 changes: 3 additions & 4 deletions lean/commands/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
from pathlib import Path
from typing import Optional
import click

from lean.click import LeanCommand, PathParameter
from lean.constants import DEFAULT_ENGINE_IMAGE
from lean.constants import DEFAULT_ENGINE_IMAGE, LEAN_ROOT_PATH
from lean.container import container
from lean.models.api import QCMinimalOrganization
from lean.models.utils import DebuggingMethod
Expand Down Expand Up @@ -61,7 +60,7 @@ def _migrate_python_pycharm(project_dir: Path) -> None:
library_dir = None

for mapping in path_mappings.findall(".//mapping"):
if mapping.get("local-root") == "$PROJECT_DIR$" and mapping.get("remote-root") == "/Lean/Launcher/bin/Debug":
if mapping.get("local-root") == "$PROJECT_DIR$" and mapping.get("remote-root") == LEAN_ROOT_PATH:
mapping.set("remote-root", "/LeanCLI")
made_changes = True

Expand Down Expand Up @@ -108,7 +107,7 @@ def _migrate_python_vscode(project_dir: Path) -> None:
library_dir = None

for mapping in config["pathMappings"]:
if mapping["localRoot"] == "${workspaceFolder}" and mapping["remoteRoot"] == "/Lean/Launcher/bin/Debug":
if mapping["localRoot"] == "${workspaceFolder}" and mapping["remoteRoot"] == LEAN_ROOT_PATH:
mapping["remoteRoot"] = "/LeanCLI"
made_changes = True

Expand Down
16 changes: 12 additions & 4 deletions lean/commands/cloud/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@

import webbrowser
from typing import Optional

import click

from lean.click import LeanCommand
from lean.container import container

from pathlib import Path
from lean.models.errors import RequestFailedError

@click.command(cls=LeanCommand)
@click.argument("project", type=str)
Expand All @@ -43,7 +42,16 @@ def backtest(project: str, name: Optional[str], push: bool, open_browser: bool)
logger = container.logger()

cloud_project_manager = container.cloud_project_manager()
cloud_project = cloud_project_manager.get_cloud_project(project, push)
try:
cloud_project = cloud_project_manager.get_cloud_project(project, push)
except RuntimeError as e:
if cloud_project_manager._project_config_manager.try_get_project_config(Path.cwd() / project,
cloud_project_manager._path_manager):
error_message = f'No project with the given name or id "{project}" found in your cloud projects.'
error_message += f" Please use `lean cloud backtest --push {project}` to backtest in cloud."
else:
error_message = f'No project with the given name or id "{project}" found in your cloud or local projects.'
raise RuntimeError(error_message)

if name is None:
name = container.name_generator().generate_name()
Expand Down
5 changes: 3 additions & 2 deletions lean/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def init() -> None:
cli_config_manager = container.cli_config_manager()
if cli_config_manager.default_language.get_value() is None:
default_language = click.prompt("What should the default language for new projects be?",
default=cli_config_manager.default_language.default_value,
type=click.Choice(cli_config_manager.default_language.allowed_values))
cli_config_manager.default_language.set_value(default_language)

Expand All @@ -121,8 +122,8 @@ def init() -> None:
- {DEFAULT_DATA_DIRECTORY_NAME}/ contains the data that is used when running the LEAN engine locally
The following documentation pages may be useful:
- Setting up local autocomplete: https://www.lean.io/docs/lean-cli/projects/autocomplete
- Synchronizing projects with the cloud: https://www.lean.io/docs/lean-cli/projects/cloud-synchronization
- Setting up local autocomplete: https://www.lean.io/docs/v2/lean-cli/projects/autocomplete
- Synchronizing projects with the cloud: https://www.lean.io/docs/v2/lean-cli/projects/cloud-synchronization
Here are some commands to get you going:
- Run `lean create-project "My Project"` to create a new project with starter code
Expand Down
9 changes: 5 additions & 4 deletions lean/commands/research.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
from docker.errors import APIError
from docker.types import Mount
from lean.click import LeanCommand, PathParameter
from lean.constants import DEFAULT_RESEARCH_IMAGE
from lean.constants import DEFAULT_RESEARCH_IMAGE, LEAN_ROOT_PATH
from lean.container import container
from lean.models.data_providers import QuantConnectDataProvider, all_data_providers
from lean.components.util.name_extraction import convert_to_class_name


def _check_docker_output(chunk: str, port: int) -> None:
"""Checks the output of the Docker container and opens the browser if Jupyter Lab has started.
Expand Down Expand Up @@ -80,7 +81,7 @@ def research(project: Path,

lean_config_manager = container.lean_config_manager()
lean_config = lean_config_manager.get_complete_lean_config("backtesting", algorithm_file, None)
lean_config["composer-dll-directory"] = "/Lean/Launcher/bin/Debug"
lean_config["composer-dll-directory"] = LEAN_ROOT_PATH
lean_config["research-object-store-name"] = algorithm_name

if download_data:
Expand All @@ -103,7 +104,7 @@ def research(project: Path,

# Mount the config in the notebooks directory as well
local_config_path = next(m["Source"] for m in run_options["mounts"] if m["Target"].endswith("config.json"))
run_options["mounts"].append(Mount(target="/Lean/Launcher/bin/Debug/Notebooks/config.json",
run_options["mounts"].append(Mount(target=f"{LEAN_ROOT_PATH}/Notebooks/config.json",
source=str(local_config_path),
type="bind",
read_only=True))
Expand All @@ -120,7 +121,7 @@ def research(project: Path,

# Mount the project to the notebooks directory
run_options["volumes"][str(project)] = {
"bind": "/Lean/Launcher/bin/Debug/Notebooks",
"bind": f"{LEAN_ROOT_PATH}/Notebooks",
"mode": "rw"
}

Expand Down
3 changes: 2 additions & 1 deletion lean/components/api/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ def _parse_response(self, response: requests.Response) -> Any:
if "errors" in data and len(data["errors"]) > 0:
if data["errors"][0].startswith("Hash doesn't match."):
raise AuthenticationError()

if data["errors"][0].startswith('UserID not valid'):
data["errors"].append('Please login to your account. https://www.quantconnect.com/docs/v2/lean-cli/api-reference/lean-login')
raise RequestFailedError(response, "\n".join(data["errors"]))

if "messages" in data and len(data["messages"]) > 0:
Expand Down
4 changes: 1 addition & 3 deletions lean/components/cloud/cloud_project_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ def get_cloud_project(self, input: str, push: bool) -> QCProject:
"""
# If the given input is a valid project directory, we try to use that project
local_path = Path.cwd() / input
if self._path_manager.is_path_valid(local_path) \
and local_path.is_dir() \
and self._project_config_manager.get_project_config(local_path).file.exists():
if self._project_config_manager.try_get_project_config(local_path, self._path_manager):
if push:
self._push_manager.push_projects([local_path])

Expand Down
3 changes: 2 additions & 1 deletion lean/components/config/cli_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def __init__(self, general_storage: Storage, credentials_storage: Storage) -> No
"The default language used when creating new projects.",
["python", "csharp"],
False,
general_storage)
general_storage,
"python")

self.engine_image = Option("engine-image",
f"The Docker image used when running the LEAN engine ({DEFAULT_ENGINE_IMAGE} if not set).",
Expand Down
14 changes: 14 additions & 0 deletions lean/components/config/project_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from lean.components.config.storage import Storage
from lean.components.util.xml_manager import XMLManager
from lean.components.util.path_manager import PathManager
from lean.constants import PROJECT_CONFIG_FILE_NAME
from lean.models.utils import CSharpLibrary

Expand All @@ -31,6 +32,19 @@ def __init__(self, xml_manager: XMLManager) -> None:
"""
self._xml_manager = xml_manager

def try_get_project_config(self, project_directory: Path, path_manager: PathManager) -> Storage:
"""Returns a Storage instance to get/set the configuration for a project.
:param project_directory: the path to the project to retrieve the configuration of
:return: the Storage instance containing the project-specific configuration of the given project
"""
if path_manager.is_path_valid(project_directory) \
and project_directory.is_dir() \
and self.get_project_config(project_directory).file.exists():
return Storage(str(project_directory / PROJECT_CONFIG_FILE_NAME))
else:
return False

def get_project_config(self, project_directory: Path) -> Storage:
"""Returns a Storage instance to get/set the configuration for a project.
Expand Down
9 changes: 5 additions & 4 deletions lean/components/docker/lean_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
from lean.components.util.project_manager import ProjectManager
from lean.components.util.temp_manager import TempManager
from lean.components.util.xml_manager import XMLManager
from lean.constants import MODULES_DIRECTORY, TERMINAL_LINK_PRODUCT_ID
from lean.constants import MODULES_DIRECTORY, TERMINAL_LINK_PRODUCT_ID, LEAN_ROOT_PATH
from lean.models.docker import DockerImage
from lean.models.utils import DebuggingMethod


class LeanRunner:
"""The LeanRunner class contains the code that runs the LEAN engine locally."""

Expand Down Expand Up @@ -314,7 +315,7 @@ def get_basic_docker_config(self,
file.write(json.dumps(lean_config, indent=4))

# Mount the Lean config
run_options["mounts"].append(Mount(target="/Lean/Launcher/bin/Debug/config.json",
run_options["mounts"].append(Mount(target=f"{LEAN_ROOT_PATH}/config.json",
source=str(config_path),
type="bind",
read_only=True))
Expand Down Expand Up @@ -495,7 +496,7 @@ def set_up_csharp_options(self, project_dir: Path, run_options: Dict[str, Any],
# Copy over the algorithm DLL
# Copy over the project reference DLLs'
# Copy over all output DLLs that don't already exist in /Lean/Launcher/bin/Debug
run_options["commands"].append("cp -R -n /Compile/bin/. /Lean/Launcher/bin/Debug/")
run_options["commands"].append(f"cp -R -n /Compile/bin/. {LEAN_ROOT_PATH}/")

# Copy over all library DLLs that don't already exist in /Lean/Launcher/bin/Debug
# CopyLocalLockFileAssemblies does not copy the OS-specific DLLs to the output directory
Expand Down Expand Up @@ -652,7 +653,7 @@ def _ensure_csproj_uses_correct_lean(self,
package_reference.clear()

package_reference.tag = "Reference"
package_reference.set("Include", "/Lean/Launcher/bin/Debug/*.dll")
package_reference.set("Include", f"{LEAN_ROOT_PATH}/*.dll")
package_reference.append(self._xml_manager.parse("<Private>False</Private>"))

include_added = True
Expand Down
3 changes: 3 additions & 0 deletions lean/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

# Due to the way the filesystem is mocked in unit tests, values should not be Path instances.

# The file in which general CLI configuration is stored
LEAN_ROOT_PATH = "/Lean/Launcher/bin/Debug"

# The file in which general CLI configuration is stored
GENERAL_CONFIG_PATH = str(Path("~/.lean/config").expanduser())

Expand Down
5 changes: 4 additions & 1 deletion lean/models/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,19 @@ def __init__(self,
description: str,
allowed_values: List[str],
is_sensitive: bool,
storage: Storage) -> None:
storage: Storage,
default_value: str = None) -> None:
"""Creates a new ChoiceOption instance.
:param key: the name of the key of the option in the given file, should use hyphens for separation
:param description: a display-friendly description of the option
:param allowed_values: the values which can be set
:param is_sensitive: whether the contents of this option may be logged without masking it
:param storage: the Storage instance to store this option in
:param default_value: the default value to use for the choices
"""
self.allowed_values = allowed_values
self.default_value = default_value

if description.endswith("."):
description = description[:-1]
Expand Down
4 changes: 2 additions & 2 deletions tests/commands/test_optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from lean.commands import lean
from lean.components.config.storage import Storage
from lean.constants import DEFAULT_ENGINE_IMAGE
from lean.constants import DEFAULT_ENGINE_IMAGE, LEAN_ROOT_PATH
from lean.container import container
from lean.models.docker import DockerImage
from lean.models.optimizer import (OptimizationConstraint, OptimizationExtremum, OptimizationParameter,
Expand Down Expand Up @@ -136,7 +136,7 @@ def test_optimize_mounts_lean_config() -> None:
docker_manager.run_image.assert_called_once()
args, kwargs = docker_manager.run_image.call_args

assert any([mount["Target"] == "/Lean/Launcher/bin/Debug/config.json" for mount in kwargs["mounts"]])
assert any([mount["Target"] == f"{LEAN_ROOT_PATH}/config.json" for mount in kwargs["mounts"]])


def test_optimize_mounts_data_directory() -> None:
Expand Down
8 changes: 4 additions & 4 deletions tests/commands/test_research.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from dependency_injector import providers

from lean.commands import lean
from lean.constants import DEFAULT_RESEARCH_IMAGE
from lean.constants import DEFAULT_RESEARCH_IMAGE, LEAN_ROOT_PATH
from lean.container import container
from lean.models.docker import DockerImage
from tests.test_helpers import create_fake_lean_cli_directory
Expand Down Expand Up @@ -56,8 +56,8 @@ def test_research_mounts_lean_config_to_notebooks_directory_as_well() -> None:
docker_manager.run_image.assert_called_once()
args, kwargs = docker_manager.run_image.call_args

lean_config = next(m["Source"] for m in kwargs["mounts"] if m["Target"] == "/Lean/Launcher/bin/Debug/config.json")
assert any(m["Source"] == lean_config and m["Target"] == "/Lean/Launcher/bin/Debug/Notebooks/config.json" for m in
lean_config = next(m["Source"] for m in kwargs["mounts"] if m["Target"] == f"{LEAN_ROOT_PATH}/config.json")
assert any(m["Source"] == lean_config and m["Target"] == f"{LEAN_ROOT_PATH}/Notebooks/config.json" for m in
kwargs["mounts"])


Expand All @@ -77,7 +77,7 @@ def test_research_adds_credentials_to_project_config() -> None:
docker_manager.run_image.assert_called_once()
args, kwargs = docker_manager.run_image.call_args

mount = [m for m in kwargs["mounts"] if m["Target"] == "/Lean/Launcher/bin/Debug/Notebooks/config.json"][0]
mount = [m for m in kwargs["mounts"] if m["Target"] == f"{LEAN_ROOT_PATH}/Notebooks/config.json"][0]

with open(mount["Source"]) as file:
config = json.load(file)
Expand Down
4 changes: 2 additions & 2 deletions tests/components/docker/test_lean_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from lean.components.util.project_manager import ProjectManager
from lean.components.util.temp_manager import TempManager
from lean.components.util.xml_manager import XMLManager
from lean.constants import DEFAULT_ENGINE_IMAGE
from lean.constants import DEFAULT_ENGINE_IMAGE, LEAN_ROOT_PATH
from lean.models.utils import DebuggingMethod
from lean.models.docker import DockerImage
from lean.models.modules import NuGetPackage
Expand Down Expand Up @@ -163,7 +163,7 @@ def test_run_lean_mounts_config_file() -> None:
docker_manager.run_image.assert_called_once()
args, kwargs = docker_manager.run_image.call_args

assert any([mount["Target"] == "/Lean/Launcher/bin/Debug/config.json" for mount in kwargs["mounts"]])
assert any([mount["Target"] == f"{LEAN_ROOT_PATH}/config.json" for mount in kwargs["mounts"]])


def test_run_lean_mounts_data_directory() -> None:
Expand Down

0 comments on commit 2a2c927

Please sign in to comment.