Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions lean/components/docker/lean_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,13 @@ def get_basic_docker_config(self,

# Update all hosts that need to point to the host's localhost to host.docker.internal so they resolve properly
# TODO: we should remove it or add to config json
for key in ["quant-bridge-server-host"]:
if key not in lean_config:
continue
for config in self._get_config_scopes(lean_config):
for key in ["quant-bridge-server-host"]:
if key not in config:
continue

if lean_config[key] == "localhost" or lean_config[key] == "127.0.0.1":
lean_config[key] = "host.docker.internal"
if config[key] == "localhost" or config[key] == "127.0.0.1":
config[key] = "host.docker.internal"

# Set up modules
set_up_common_csharp_options_called = self._setup_installed_packages(run_options, image, lean_config)
Expand Down Expand Up @@ -287,12 +288,13 @@ def get_basic_docker_config_without_algo(self,

# Update all hosts that need to point to the host's localhost to host.docker.internal so they resolve properly
# TODO: we should remove it or add to config json
for key in ["quant-bridge-server-host"]:
if key not in lean_config:
continue
for config in self._get_config_scopes(lean_config):
for key in ["quant-bridge-server-host"]:
if key not in config:
continue

if lean_config[key] == "localhost" or lean_config[key] == "127.0.0.1":
lean_config[key] = "host.docker.internal"
if config[key] == "localhost" or config[key] == "127.0.0.1":
config[key] = "host.docker.internal"

# Set up modules
self._setup_installed_packages(run_options, image, lean_config, target_path)
Expand Down Expand Up @@ -415,11 +417,15 @@ def _mount_common_directories(self,
("transaction-log", cli_root_dir),
("quant-bridge-symbol-map-file", cli_root_dir / DEFAULT_DATA_DIRECTORY_NAME / "symbol-properties")
]
scopes = self._get_config_scopes(lean_config)
for key, base_path in files_to_mount:
if key not in lean_config or lean_config[key] == "":
# The same key can be set at the top level and in the selected environment:
# mount it once, with the environment's value taking precedence
configs = [config for config in scopes if key in config and config[key] != ""]
if not configs:
continue

lean_config_entry = Path(lean_config[key])
lean_config_entry = Path(configs[-1][key])
local_path = lean_config_entry if lean_config_entry.is_absolute() else base_path / lean_config_entry
if not local_path.exists():
local_path.parent.mkdir(parents=True, exist_ok=True)
Expand All @@ -430,7 +436,18 @@ def _mount_common_directories(self,
type="bind",
read_only=False))

lean_config[key] = f"/Files/{key}"
for config in configs:
config[key] = f"/Files/{key}"

@staticmethod
def _get_config_scopes(lean_config: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Returns the top level of the Lean config plus the selected environment's entries, if any."""
scopes = [lean_config]
if "environment" in lean_config and "environments" in lean_config:
environment = lean_config["environments"].get(lean_config["environment"])
if environment is not None:
scopes.append(environment)
return scopes


def _initialize_run_options(self, detach: bool, docker_project_config: Dict[str, Any], debugging_method: Optional[DebuggingMethod]):
Expand Down
72 changes: 72 additions & 0 deletions tests/components/docker/test_lean_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,3 +831,75 @@ def test_lean_runner_parses_environment_values_containing_equals_from_extra_dock
{"environment": ["JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8", "TOKEN=abc=="]})

assert run_options["environment"] == {"JAVA_TOOL_OPTIONS": "-Dfile.encoding=UTF-8", "TOKEN": "abc=="}


def _run_with_environment_config(lean_config: dict):
create_fake_lean_cli_directory()

docker_manager = mock.Mock()
docker_manager.run_image.return_value = True

lean_runner = create_lean_runner(docker_manager)
lean_runner.run_lean(lean_config,
"lean-cli",
Path.cwd() / "Python Project" / "main.py",
Path.cwd() / "output",
ENGINE_IMAGE,
None,
False,
False)

docker_manager.run_image.assert_called_once()
return docker_manager.run_image.call_args


@pytest.mark.parametrize("host", ["localhost", "127.0.0.1"])
def test_run_lean_rewrites_localhost_quant_bridge_host_in_selected_environment(host: str) -> None:
lean_config = {
"environment": "lean-cli",
"environments": {"lean-cli": {"quant-bridge-server-host": host}}
}

_run_with_environment_config(lean_config)

assert lean_config["environments"]["lean-cli"]["quant-bridge-server-host"] == "host.docker.internal"


def test_run_lean_mounts_quant_bridge_symbol_map_file_from_selected_environment() -> None:
lean_config = {
"environment": "lean-cli",
"environments": {"lean-cli": {"quant-bridge-symbol-map-file": "quant-bridge-symbol-map.json"}}
}

args, kwargs = _run_with_environment_config(lean_config)

from lean.container import container
cli_root_dir = container.lean_config_manager.get_cli_root_directory()
expected_source = cli_root_dir / DEFAULT_DATA_DIRECTORY_NAME / "symbol-properties" / "quant-bridge-symbol-map.json"

assert any([
Path(mount["Source"]) == expected_source and
mount["Target"] == "/Files/quant-bridge-symbol-map-file"
for mount in kwargs["mounts"]
])
assert lean_config["environments"]["lean-cli"]["quant-bridge-symbol-map-file"] == "/Files/quant-bridge-symbol-map-file"


def test_run_lean_mounts_quant_bridge_symbol_map_file_once_when_set_in_root_and_environment() -> None:
lean_config = {
"environment": "lean-cli",
"quant-bridge-symbol-map-file": "root-symbol-map.json",
"environments": {"lean-cli": {"quant-bridge-symbol-map-file": "env-symbol-map.json"}}
}

args, kwargs = _run_with_environment_config(lean_config)

from lean.container import container
cli_root_dir = container.lean_config_manager.get_cli_root_directory()
expected_source = cli_root_dir / DEFAULT_DATA_DIRECTORY_NAME / "symbol-properties" / "env-symbol-map.json"

targets = [mount["Target"] for mount in kwargs["mounts"]]
assert targets.count("/Files/quant-bridge-symbol-map-file") == 1
assert any(Path(mount["Source"]) == expected_source for mount in kwargs["mounts"])
assert lean_config["quant-bridge-symbol-map-file"] == "/Files/quant-bridge-symbol-map-file"
assert lean_config["environments"]["lean-cli"]["quant-bridge-symbol-map-file"] == "/Files/quant-bridge-symbol-map-file"