From aa1f16d2ab7a99ff542031c83aedaf842b017386 Mon Sep 17 00:00:00 2001 From: antazoey Date: Wed, 8 May 2024 11:20:34 -0600 Subject: [PATCH] fix: issue where `project` was not honored in `console` function (#2072) --- src/ape_console/_cli.py | 29 +++++++++++++++++------------ tests/functional/test_console.py | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/ape_console/_cli.py b/src/ape_console/_cli.py index ffba704d2d..0c8b7d2983 100644 --- a/src/ape_console/_cli.py +++ b/src/ape_console/_cli.py @@ -4,15 +4,16 @@ import sys from importlib.machinery import SourceFileLoader from importlib.util import module_from_spec, spec_from_loader -from os import environ, getcwd +from os import environ from types import ModuleType -from typing import Any, Dict, cast +from typing import Any, Dict, Optional, cast import click import IPython from IPython.terminal.ipapp import Config as IPythonConfig from ape.cli import ConnectedProviderCommand, ape_cli_context +from ape.managers import ProjectManager from ape.utils.basemodel import ManagerAccessMixin from ape.utils.misc import _python_version from ape.version import version as ape_version @@ -49,10 +50,9 @@ def import_extras_file(file_path) -> ModuleType: def load_console_extras(**namespace: Any) -> Dict[str, Any]: """load and return namespace updates from ape_console_extras.py files if they exist""" - global_extras = ManagerAccessMixin.config_manager.DATA_FOLDER.joinpath(CONSOLE_EXTRAS_FILENAME) - project_extras = ManagerAccessMixin.config_manager.PROJECT_FOLDER.joinpath( - CONSOLE_EXTRAS_FILENAME - ) + pm = namespace.get("project", ManagerAccessMixin.project_manager) + global_extras = pm.config_manager.DATA_FOLDER.joinpath(CONSOLE_EXTRAS_FILENAME) + project_extras = pm.path.joinpath(CONSOLE_EXTRAS_FILENAME) for extras_file in [global_extras, project_extras]: if not extras_file.is_file(): @@ -88,13 +88,15 @@ def load_console_extras(**namespace: Any) -> Dict[str, Any]: return namespace -def console(project=None, verbose=None, extra_locals=None, embed=False): +def console( + project: Optional[ProjectManager] = None, + verbose: bool = False, + extra_locals: Optional[Dict] = None, + embed: bool = False, +): import ape - if not project: - # Use default project - project = ManagerAccessMixin.project_manager - + project = project or ManagerAccessMixin.project_manager banner = "" if verbose: banner = """ @@ -115,12 +117,15 @@ def console(project=None, verbose=None, extra_locals=None, embed=False): faulthandler.enable() # NOTE: In case we segfault namespace = {component: getattr(ape, component) for component in ape.__all__} + namespace["project"] = project # Use the given project. namespace["ape"] = ape + # Allows modules relative to the project. + sys.path.insert(0, f"{project.path}") + # NOTE: `ape_console_extras` only is meant to work with default namespace. # Load extras before local namespace to avoid console extras receiving # the wrong values for its arguments. - sys.path.insert(0, getcwd()) console_extras = load_console_extras(**namespace) if extra_locals: diff --git a/tests/functional/test_console.py b/tests/functional/test_console.py index 6e1d38ed9c..0c5af1cae5 100644 --- a/tests/functional/test_console.py +++ b/tests/functional/test_console.py @@ -1,5 +1,9 @@ +import sys + import pytest +from ape import Project +from ape.utils import create_tempdir from ape_console._cli import console @@ -31,3 +35,19 @@ def test_console_extras_uses_ape_namespace(mocker, mock_console, mock_ape_consol # Show the custom accounts do get used in console. assert mock_console.call_args[0][0]["accounts"] == accounts_custom + + +def test_console_custom_project(mock_console, mock_ape_console_extras): + with create_tempdir() as path: + project = Project(path) + console(project=project) + actuals = ( + mock_console.call_args[0][0]["project"], # Launch namespace + mock_ape_console_extras.call_args[1]["project"], # extras-load namespace + ) + + for actual in actuals: + assert actual == project + + # Ensure sys.path was updated correctly. + assert sys.path[0] == str(project.path)