Skip to content

Commit bfd7dfa

Browse files
committed
Validating path relative to cli root instead of the absolute one
1 parent 704083f commit bfd7dfa

15 files changed

+65
-36
lines changed

lean/click.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def __init__(self, exists: bool = False, file_okay: bool = True, dir_okay: bool
188188
def convert(self, value: str, param: Parameter, ctx: Context) -> Path:
189189
path = Path(value).expanduser().resolve()
190190

191-
if not container.path_manager.is_path_valid(path):
191+
if not container.path_manager.is_cli_path_valid(path):
192192
self.fail(f"{self._path_type} '{value}' is not a valid path.", param, ctx)
193193

194194
if self._exists and not path.exists():

lean/commands/cloud/backtest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ def backtest(project: str, name: Optional[str], push: bool, open_browser: bool)
4444
try:
4545
cloud_project = cloud_project_manager.get_cloud_project(project, push)
4646
except RuntimeError as e:
47-
if cloud_project_manager._project_config_manager.try_get_project_config(Path.cwd() / project,
48-
cloud_project_manager._path_manager):
47+
if cloud_project_manager._project_config_manager.try_get_project_config(Path.cwd() / project):
4948
error_message = f'No project with the given name or id "{project}" found in your cloud projects.'
5049
error_message += f" Please use `lean cloud backtest --push {project}` to backtest in cloud."
5150
else:

lean/commands/create_project.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,8 @@ def create_project(name: str, language: str) -> None:
292292
"https://www.lean.io/docs/v2/lean-cli/projects/project-management")
293293

294294
full_path = Path.cwd() / name
295-
cli_root_dir = container.lean_config_manager.get_cli_root_directory()
296-
relative_path = full_path.relative_to(cli_root_dir)
297295

298-
if not container.path_manager.is_path_valid(relative_path) or not container.path_manager.is_name_valid(name):
296+
if not container.path_manager.is_cli_path_valid(full_path) or not container.path_manager.is_name_valid(name):
299297
raise MoreInfoError(f"Invalid project name. Can only contain letters, numbers & spaces. Can not start with empty char ' ' or be a reserved name [ {', '.join(reserved_names)} ]",
300298
"https://www.lean.io/docs/v2/lean-cli/key-concepts/troubleshooting#02-Common-Errors")
301299

lean/components/cloud/cloud_project_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def get_cloud_project(self, input: str, push: bool) -> QCProject:
6666

6767
# If the given input is a valid project directory, we try to use that project
6868
local_path = Path.cwd() / input
69-
if self._project_config_manager.try_get_project_config(local_path, self._path_manager):
69+
if self._project_config_manager.try_get_project_config(local_path):
7070
if push:
7171
self._push_manager.push_projects([local_path])
7272

lean/components/config/project_config_manager.py

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

1717
from lean.components.config.storage import Storage
1818
from lean.components.util.xml_manager import XMLManager
19-
from lean.components.util.path_manager import PathManager
2019
from lean.constants import PROJECT_CONFIG_FILE_NAME
2120
from lean.models.utils import CSharpLibrary
2221

@@ -31,15 +30,13 @@ def __init__(self, xml_manager: XMLManager) -> None:
3130
"""
3231
self._xml_manager = xml_manager
3332

34-
def try_get_project_config(self, project_directory: Path, path_manager: PathManager) -> Storage:
33+
def try_get_project_config(self, project_directory: Path) -> Storage:
3534
"""Returns a Storage instance to get/set the configuration for a project.
3635
3736
:param project_directory: the path to the project to retrieve the configuration of
3837
:return: the Storage instance containing the project-specific configuration of the given project
3938
"""
40-
if path_manager.is_path_valid(project_directory) \
41-
and project_directory.is_dir() \
42-
and self.get_project_config(project_directory).file.exists():
39+
if self.get_project_config(project_directory).file.exists():
4340
return Storage(str(project_directory / PROJECT_CONFIG_FILE_NAME))
4441
else:
4542
return False

lean/components/util/library_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def is_lean_library(self, path: Path) -> bool:
5454
:param path: path to check whether it is a Lean library
5555
:return: true if the path is a Lean library path
5656
"""
57-
if not self._path_manager.is_path_valid(path):
57+
if not self._path_manager.is_cli_path_valid(path):
5858
return False
5959

6060
relative_path = self._path_manager.get_relative_path(path, self._lean_config_manager.get_cli_root_directory())

lean/components/util/path_manager.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,21 @@
1212
# limitations under the License.
1313

1414
from pathlib import Path
15+
1516
from lean.components import reserved_names, forbidden_characters
17+
from lean.components.config.lean_config_manager import LeanConfigManager
1618
from lean.components.util.platform_manager import PlatformManager
1719

20+
1821
class PathManager:
1922
"""The PathManager class provides utilities for working with paths."""
2023

21-
def __init__(self, platform_manager: PlatformManager) -> None:
24+
def __init__(self, lean_config_manager: LeanConfigManager, platform_manager: PlatformManager) -> None:
2225
"""Creates a new PathManager instance.
2326
2427
:param platform_manager: the PlatformManager used when checking which operating system is in use
2528
"""
29+
self._lean_config_manager = lean_config_manager
2630
self._platform_manager = platform_manager
2731

2832
def get_relative_path(self, destination: Path, source: Path = Path.cwd()) -> Path:
@@ -78,3 +82,14 @@ def is_path_valid(self, path: Path) -> bool:
7882
if forbidden_character in component:
7983
return False
8084
return True
85+
86+
def is_cli_path_valid(self, path: Path) -> bool:
87+
"""Returns whether the given path is a valid project path in the current operating system.
88+
89+
:param path: the path to validate
90+
:return: True if the path is valid on the current operating system, False if not
91+
"""
92+
cli_root_dir = self._lean_config_manager.get_cli_root_directory()
93+
relative_path = path.relative_to(cli_root_dir)
94+
95+
return relative_path == Path(".") or self.is_path_valid(relative_path)

lean/container.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,18 @@
1414
from typing import Union, Any
1515

1616
from lean.components.api.api_client import APIClient
17+
from lean.components.config.lean_config_manager import LeanConfigManager
18+
from lean.components.config.project_config_manager import ProjectConfigManager
19+
from lean.components.util.path_manager import PathManager
1720
from lean.components.cloud.cloud_project_manager import CloudProjectManager
1821
from lean.components.cloud.cloud_runner import CloudRunner
1922
from lean.components.cloud.data_downloader import DataDownloader
2023
from lean.components.cloud.module_manager import ModuleManager
2124
from lean.components.cloud.pull_manager import PullManager
2225
from lean.components.cloud.push_manager import PushManager
2326
from lean.components.config.cli_config_manager import CLIConfigManager
24-
from lean.components.config.lean_config_manager import LeanConfigManager
2527
from lean.components.config.optimizer_config_manager import OptimizerConfigManager
2628
from lean.components.config.output_config_manager import OutputConfigManager
27-
from lean.components.config.project_config_manager import ProjectConfigManager
2829
from lean.components.config.storage import Storage
2930
from lean.components.docker.docker_manager import DockerManager
3031
from lean.components.docker.lean_runner import LeanRunner
@@ -34,7 +35,6 @@
3435
from lean.components.util.market_hours_database import MarketHoursDatabase
3536
from lean.components.util.name_generator import NameGenerator
3637
from lean.components.util.organization_manager import OrganizationManager
37-
from lean.components.util.path_manager import PathManager
3838
from lean.components.util.platform_manager import PlatformManager
3939
from lean.components.util.project_manager import ProjectManager
4040
from lean.components.util.task_manager import TaskManager
@@ -63,7 +63,6 @@ def initialize(self,
6363
self.platform_manager = PlatformManager()
6464
self.task_manager = TaskManager(self.logger)
6565
self.name_generator = NameGenerator()
66-
self.path_manager = PathManager(self.platform_manager)
6766
self.temp_manager = TempManager()
6867
self.xml_manager = XMLManager()
6968
self.http_client = HTTPClient(self.logger)
@@ -92,6 +91,7 @@ def initialize(self,
9291
self.project_config_manager,
9392
self.module_manager,
9493
self.cache_storage)
94+
self.path_manager = PathManager(self.lean_config_manager, self.platform_manager)
9595
self.output_config_manager = OutputConfigManager(self.lean_config_manager)
9696
self.optimizer_config_manager = OptimizerConfigManager(self.logger)
9797

tests/commands/test_build.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@
2222
from lean.models.docker import DockerImage
2323
from lean.constants import CUSTOM_FOUNDATION, CUSTOM_RESEARCH, CUSTOM_ENGINE
2424
from tests.conftest import initialize_container
25+
from tests.test_helpers import create_fake_lean_cli_directory
2526

2627
CUSTOM_FOUNDATION_IMAGE = DockerImage(name=CUSTOM_FOUNDATION, tag="latest")
2728
CUSTOM_ENGINE_IMAGE = DockerImage(name=CUSTOM_ENGINE, tag="latest")
2829
CUSTOM_RESEARCH_IMAGE = DockerImage(name=CUSTOM_RESEARCH, tag="latest")
2930

3031

32+
@pytest.fixture(autouse=True)
33+
def create_fake_cli_directory() -> None:
34+
create_fake_lean_cli_directory()
35+
36+
3137
def create_fake_repositories() -> None:
3238
lean_dir = Path.cwd() / "Lean"
3339

tests/commands/test_create_project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def test_create_project_aborts_when_path_invalid() -> None:
153153
create_fake_lean_cli_directory()
154154

155155
path_manager = mock.Mock()
156-
path_manager.is_path_valid.return_value = False
156+
path_manager.is_cli_path_valid.return_value = False
157157
container.path_manager= path_manager
158158

159159
result = CliRunner().invoke(lean, ["create-project", "--language", "python", "My First Project"])

0 commit comments

Comments
 (0)