diff --git a/src/ape/_cli.py b/src/ape/_cli.py index d8a193ee41..3a61812a33 100644 --- a/src/ape/_cli.py +++ b/src/ape/_cli.py @@ -6,20 +6,16 @@ from gettext import gettext from importlib.metadata import entry_points from pathlib import Path -from typing import TYPE_CHECKING, Any, Optional +from typing import Any, Optional from warnings import catch_warnings, simplefilter import click -import rich import yaml from ape.cli.options import ape_cli_context -from ape.exceptions import Abort, ApeException, ConfigError, handle_ape_exception +from ape.exceptions import Abort, ApeException, handle_ape_exception from ape.logging import logger -if TYPE_CHECKING: - from click import Context - _DIFFLIB_CUT_OFF = 0.6 @@ -39,30 +35,9 @@ def display_config(ctx, param, value): ctx.exit() # NOTE: Must exit to bypass running ApeCLI -def _validate_config(): - from ape.utils.basemodel import ManagerAccessMixin as access - - project = access.local_project - try: - _ = project.config - except ConfigError as err: - rich.print(err) - # Exit now to avoid weird problems. - sys.exit(1) - - class ApeCLI(click.MultiCommand): _CLI_GROUP_NAME = "ape_cli_subcommands" - def parse_args(self, ctx: "Context", args: list[str]) -> list[str]: - # Validate the config before any argument parsing, - # as arguments may utilize config. - if "--help" not in args and args != []: - # perf: don't bother w/ config if only doing --help. - _validate_config() - - return super().parse_args(ctx, args) - def format_commands(self, ctx, formatter) -> None: from ape.plugins._utils import PluginMetadataList diff --git a/src/ape/managers/project.py b/src/ape/managers/project.py index 619a52c7d9..2f6f658945 100644 --- a/src/ape/managers/project.py +++ b/src/ape/managers/project.py @@ -2214,8 +2214,10 @@ def __getattr__(self, item: str) -> Any: "missing compilers for extensions: " + f'{", ".join(sorted(missing_exts))}?' ) - err.args = (message,) - raise # The same exception (keep the stack the same height). + # NOTE: Purposely discard the stack-trace and raise a new exception. + # This shows a better stack-trace to the user (rather than weird + # BaseModel internals). + raise AttributeError(message) @cached_property def path(self) -> Path: diff --git a/src/ape_init/_cli.py b/src/ape_init/_cli.py index 1102dbb9e7..5b57012a23 100644 --- a/src/ape_init/_cli.py +++ b/src/ape_init/_cli.py @@ -5,7 +5,6 @@ from click import BadParameter from ape.cli.options import ape_cli_context -from ape.utils._github import github_client GITIGNORE_CONTENT = """ # Ape stuff @@ -45,12 +44,15 @@ def validate_github_repo(ctx, param, value): help="Clone a template from Github", callback=validate_github_repo, ) -def cli(cli_ctx, github): +@click.option("--name", "project_name", prompt=True, help="A project name") +def cli(cli_ctx, github, project_name): """ ``ape init`` allows the user to create an ape project with default folders and ape-config.yaml. """ if github: + from ape.utils._github import github_client + org, repo = github github_client.clone_repo(org, repo, Path.cwd()) shutil.rmtree(Path.cwd() / ".git", ignore_errors=True) @@ -76,6 +78,5 @@ def cli(cli_ctx, github): if ape_config.exists(): cli_ctx.logger.warning(f"'{ape_config}' exists") else: - project_name = click.prompt("Please enter project name") ape_config.write_text(f"name: {project_name}\n", encoding="utf8") cli_ctx.logger.success(f"{project_name} is written in ape-config.yaml") diff --git a/tests/functional/test_project.py b/tests/functional/test_project.py index 29f9dfb03e..bb0ee3de41 100644 --- a/tests/functional/test_project.py +++ b/tests/functional/test_project.py @@ -209,9 +209,16 @@ def test_getattr(tmp_project): def test_getattr_not_exists(tmp_project): - with pytest.raises(AttributeError): + expected = ( + r"'LocalProject' object has no attribute 'nope'\. Also checked extra\(s\) 'contracts'\." + ) + with pytest.raises(AttributeError, match=expected) as err: _ = tmp_project.nope + # Was the case where the last entry was from Ape's basemodel stuff. + # Now, it points at the project manager last. + assert "ape/managers/project.py:" in repr(err.traceback[-1]) + def test_getattr_detects_changes(tmp_project): source_id = tmp_project.Other.contract_type.source_id