diff --git a/src/ape/managers/project.py b/src/ape/managers/project.py index 4f3603e9fe..ae62a12b96 100644 --- a/src/ape/managers/project.py +++ b/src/ape/managers/project.py @@ -18,7 +18,7 @@ from ape.api.projects import ApeProject, DependencyAPI, ProjectAPI from ape.contracts import ContractContainer, ContractInstance -from ape.exceptions import APINotImplementedError, ChainError, ProjectError +from ape.exceptions import APINotImplementedError, ChainError, CompilerError, ProjectError from ape.logging import logger from ape.managers.base import BaseManager from ape.managers.config import ApeConfig @@ -1891,6 +1891,14 @@ def reconfigure(self, **overrides): self.account_manager.test_accounts.reset() def extract_manifest(self) -> PackageManifest: + # Attempt to compile, if needed. + try: + self.load_contracts() + except CompilerError as err: + # Some manifest-based projects may not require compiling, + # such as OpenZeppelin or snekmate. + logger.warning(err) + return self.manifest def clean(self): @@ -2460,9 +2468,9 @@ def extract_manifest(self) -> PackageManifest: sources = dict(self.sources) contract_types = { - n: ct - for n, ct in (self.manifest.contract_types or {}).items() - if ct.source_id in sources + n: c.contract_type + for n, c in self.load_contracts().items() + if c.contract_type.source_id in sources } # Add any remaining data to the manifest here. diff --git a/tests/functional/test_project.py b/tests/functional/test_project.py index 129be09143..9528f147bb 100644 --- a/tests/functional/test_project.py +++ b/tests/functional/test_project.py @@ -293,6 +293,18 @@ def test_extract_manifest_excludes_cache(tmp_project): assert ".cache/subdir/CacheFile.json" not in (manifest.sources or {}) +def test_extract_manifest_compiles(tmp_project): + tmp_project.manifest.contract_types = {} # Not compiled. + actual = tmp_project.extract_manifest() + assert actual.contract_types # Fails if empty + + +def test_extract_manifest_from_manifest_project(project_from_manifest): + project_from_manifest.manifest.contract_types = {} # Not compiled. + manifest = project_from_manifest.extract_manifest() + assert "FooContractFromManifest" in manifest.contract_types + + def test_exclusions(tmp_project): exclusions = ["Other.json", "*Excl*"] exclude_config = {"compile": {"exclude": exclusions}} @@ -417,6 +429,35 @@ def test_load_contracts_output_abi(tmp_project): assert isinstance(data[0], dict) +def test_load_contracts_use_cache(mocker, tmp_project): + """ + Showing the 'use_cache=bool' kwarg works. + """ + compile_spy = mocker.spy(tmp_project.contracts, "_compile") + + tmp_project.manifest.contract_types = {} # Force initial compile. + contracts = tmp_project.load_contracts(use_cache=True) + assert "Other" in contracts # Other.json contract. + assert "Project" in contracts # Project.json contract. + assert compile_spy.call_args_list[-1][-1]["use_cache"] is True + + # Show they get added to the manifest. + assert "Other" in tmp_project.manifest.contract_types + assert "Project" in tmp_project.manifest.contract_types + + # Showe we can use the cache again (no compiling!) + contracts = tmp_project.load_contracts(use_cache=True) + assert "Other" in contracts # Other.json contract. + assert "Project" in contracts # Project.json contract. + assert compile_spy.call_args_list[-1][-1]["use_cache"] is True + + # Show force-recompiles. + contracts = tmp_project.load_contracts(use_cache=False) + assert "Other" in contracts # Other.json contract. + assert "Project" in contracts # Project.json contract. + assert compile_spy.call_args_list[-1][-1]["use_cache"] is False + + def test_manifest_path(tmp_project): assert tmp_project.manifest_path == tmp_project.path / ".build" / "__local__.json"