diff --git a/src/ape/api/config.py b/src/ape/api/config.py index 0c2e5cbd7a..e22a383b59 100644 --- a/src/ape/api/config.py +++ b/src/ape/api/config.py @@ -402,10 +402,10 @@ def write_to_disk(self, destination: Path, replace: bool = False): yaml.safe_dump(data, file) elif destination.suffix == ".json": - destination.write_text(self.model_dump_json(by_alias=True)) + destination.write_text(self.model_dump_json(by_alias=True), encoding="utf8") else: - raise ValueError(f"Unsupported destination file type {destination}.") + raise ConfigError(f"Unsupported destination file type '{destination}'.") def _get_compile_configs_from_manifest(manifest: PackageManifest) -> dict[str, dict]: diff --git a/src/ape/managers/chain.py b/src/ape/managers/chain.py index 2324b7179c..836dc88385 100644 --- a/src/ape/managers/chain.py +++ b/src/ape/managers/chain.py @@ -1345,22 +1345,22 @@ def _get_contract_creation_from_disk(self, address: AddressType) -> Optional[Con def _cache_contract_to_disk(self, address: AddressType, contract_type: ContractType): self._contract_types_cache.mkdir(exist_ok=True, parents=True) address_file = self._contract_types_cache / f"{address}.json" - address_file.write_text(contract_type.model_dump_json()) + address_file.write_text(contract_type.model_dump_json(), encoding="utf8") def _cache_proxy_info_to_disk(self, address: AddressType, proxy_info: ProxyInfoAPI): self._proxy_info_cache.mkdir(exist_ok=True, parents=True) address_file = self._proxy_info_cache / f"{address}.json" - address_file.write_text(proxy_info.model_dump_json()) + address_file.write_text(proxy_info.model_dump_json(), encoding="utf8") def _cache_blueprint_to_disk(self, blueprint_id: str, contract_type: ContractType): self._blueprint_cache.mkdir(exist_ok=True, parents=True) blueprint_file = self._blueprint_cache / f"{blueprint_id}.json" - blueprint_file.write_text(contract_type.model_dump_json()) + blueprint_file.write_text(contract_type.model_dump_json(), encoding="utf8") def _cache_contract_creation_to_disk(self, address: AddressType, creation: ContractCreation): self._contract_creation_cache.mkdir(exist_ok=True, parents=True) path = self._contract_creation_cache / f"{address}.json" - path.write_text(creation.model_dump_json()) + path.write_text(creation.model_dump_json(), encoding="utf8") def _load_deployments_cache(self) -> dict: return ( diff --git a/src/ape/managers/project.py b/src/ape/managers/project.py index 26f4ab15ca..5eff5360cc 100644 --- a/src/ape/managers/project.py +++ b/src/ape/managers/project.py @@ -883,7 +883,7 @@ def cache_api(self, api: DependencyAPI) -> Path: exclude_defaults=True, ) - api_file.write_text(json_text) + api_file.write_text(json_text, encoding="utf8") return api_file def remove(self, package_id: str, version: str): @@ -1605,7 +1605,7 @@ def unpack(self, destination: Path, config_override: Optional[dict] = None) -> " for source_id, src in sources.items(): path = destination / source_id path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(str(src.content)) + path.write_text(str(src.content), encoding="utf8") # Unpack config file. self.config.write_to_disk(destination / "ape-config.yaml") @@ -1899,7 +1899,7 @@ def track(self, contract: ContractInstance): # NOTE: missing_ok=True to handle race condition. destination.unlink(missing_ok=True) - destination.write_text(artifact.model_dump_json()) + destination.write_text(artifact.model_dump_json(), encoding="utf8") def __iter__(self) -> Iterator[EthPMContractInstance]: """ @@ -2318,7 +2318,7 @@ def update_manifest(self, **kwargs): self.manifest_path.unlink(missing_ok=True) manifest_text = self.manifest.model_dump_json(mode="json", by_alias=True) self.manifest_path.parent.mkdir(parents=True, exist_ok=True) - self.manifest_path.write_text(manifest_text) + self.manifest_path.write_text(manifest_text, encoding="utf8") def load_contracts( self, *source_ids: Union[str, Path], use_cache: bool = True @@ -2414,7 +2414,7 @@ def _update_contract_types(self, contract_types: dict[str, ContractType]): abi_json = json.dumps( [x.model_dump_json(by_alias=True, mode="json") for x in ct.abi] ) - file.write_text(abi_json) + file.write_text(abi_json, encoding="utf8") def _find_directory_with_extension( diff --git a/src/ape/types/coverage.py b/src/ape/types/coverage.py index f343b04c93..760d20935a 100644 --- a/src/ape/types/coverage.py +++ b/src/ape/types/coverage.py @@ -812,7 +812,7 @@ def write_xml(self, path: Path): path = path / "coverage.xml" path.unlink(missing_ok=True) - path.write_text(xml) + path.write_text(xml, encoding="utf8") def write_html(self, path: Path, verbose: bool = False): if not (html := self.get_html(verbose=verbose)): @@ -833,7 +833,7 @@ def write_html(self, path: Path, verbose: bool = False): # Create new index.html. index = html_path / "index.html" index.unlink(missing_ok=True) - index.write_text(html) + index.write_text(html, encoding="utf8") favicon = html_path / "favicon.ico" if not favicon.is_file(): @@ -862,7 +862,7 @@ def write_html(self, path: Path, verbose: bool = False): css = html_path / "styles.css" css.unlink(missing_ok=True) - css.write_text(_CSS) + css.write_text(_CSS, encoding="utf8") def get_html(self, verbose: bool = False) -> str: """ diff --git a/src/ape_accounts/accounts.py b/src/ape_accounts/accounts.py index 60ec34d371..d4ab603534 100644 --- a/src/ape_accounts/accounts.py +++ b/src/ape_accounts/accounts.py @@ -115,7 +115,7 @@ def public_key(self) -> HexBytes: key_file_data = self.keyfile key_file_data["public_key"] = publicKey[2:] - self.keyfile_path.write_text(json.dumps(key_file_data)) + self.keyfile_path.write_text(json.dumps(key_file_data), encoding="utf8") return HexBytes(bytes.fromhex(publicKey[2:])) @@ -148,7 +148,9 @@ def change_password(self): key = self.__key passphrase = self._prompt_for_passphrase("Create new passphrase", confirmation_prompt=True) - self.keyfile_path.write_text(json.dumps(EthAccount.encrypt(key, passphrase))) + self.keyfile_path.write_text( + json.dumps(EthAccount.encrypt(key, passphrase)), encoding="utf8" + ) def delete(self): passphrase = self._prompt_for_passphrase( @@ -293,7 +295,7 @@ def _write_and_return_account(alias: str, passphrase: str, account: LocalAccount path = ManagerAccessMixin.account_manager.containers["accounts"].data_folder.joinpath( f"{alias}.json" ) - path.write_text(json.dumps(EthAccount.encrypt(account.key, passphrase))) + path.write_text(json.dumps(EthAccount.encrypt(account.key, passphrase)), encoding="utf8") return KeyfileAccount(keyfile_path=path) diff --git a/src/ape_init/_cli.py b/src/ape_init/_cli.py index dd2000b3eb..1d6fcd6673 100644 --- a/src/ape_init/_cli.py +++ b/src/ape_init/_cli.py @@ -71,12 +71,12 @@ def cli(cli_ctx, github): cli_ctx.logger.warning(f"Unable to create .gitignore: '{git_ignore_path}' file exists.") else: git_ignore_path.touch() - git_ignore_path.write_text(GITIGNORE_CONTENT.lstrip()) + git_ignore_path.write_text(GITIGNORE_CONTENT.lstrip(), encoding="utf8") ape_config = project_folder / CONFIG_FILE_NAME 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") + ape_config.write_text(f"name: {project_name}\n", encoding="utf8") cli_ctx.logger.success(f"{project_name} is written in {CONFIG_FILE_NAME}") diff --git a/src/ape_pm/dependency.py b/src/ape_pm/dependency.py index 892ad48b41..d7d0bfe94c 100644 --- a/src/ape_pm/dependency.py +++ b/src/ape_pm/dependency.py @@ -85,7 +85,7 @@ def fetch(self, destination: Path): destination.unlink(missing_ok=True) destination.parent.mkdir(parents=True, exist_ok=True) - destination.write_text(self.local.read_text()) + destination.write_text(self.local.read_text(), encoding="utf8") class GithubDependency(DependencyAPI): diff --git a/tests/conftest.py b/tests/conftest.py index 55b278288f..980f924f65 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -356,7 +356,7 @@ def _make_keyfile_account(base_path: Path, alias: str, params: dict, funder): # Corrupted from a previous test test_keyfile_path.unlink() - test_keyfile_path.write_text(json.dumps(params)) + test_keyfile_path.write_text(json.dumps(params), encoding="utf8") acct = ape.accounts.load(alias) funder.transfer(acct, "25 ETH") # Auto-fund this account return acct diff --git a/tests/functional/test_cli.py b/tests/functional/test_cli.py index 4820204e05..0867b11885 100644 --- a/tests/functional/test_cli.py +++ b/tests/functional/test_cli.py @@ -486,7 +486,7 @@ def test_contract_file_paths_argument_handles_exclude( # make a .cache file to show it is ignored. cache_file = project_with_contract.contracts_folder / ".cache" / "thing.json" cache_file.parent.mkdir(parents=True, exist_ok=True) - cache_file.write_text("FAILS IF LOADED") + cache_file.write_text("FAILS IF LOADED", encoding="utf8") result = runner.invoke(contracts_paths_cmd, "contracts") assert "Exclude.json" not in result.output diff --git a/tests/functional/test_compilers.py b/tests/functional/test_compilers.py index 26c718a08b..92542d5f24 100644 --- a/tests/functional/test_compilers.py +++ b/tests/functional/test_compilers.py @@ -85,7 +85,7 @@ def test_contract_type_collision(compilers, project_with_contract, mock_compiler new_contract = project_with_contract.path / existing_contract.source_id.replace( ".json", mock_compiler.ext ) - new_contract.write_text("foobar") + new_contract.write_text("foobar", encoding="utf8") to_compile = [existing_path, new_contract] compile = compilers.compile(to_compile, project=project_with_contract) @@ -100,7 +100,7 @@ def test_contract_type_collision(compilers, project_with_contract, mock_compiler def test_compile_with_settings(mock_compiler, compilers, project_with_contract): new_contract = project_with_contract.path / f"AMockContract{mock_compiler.ext}" - new_contract.write_text("foobar") + new_contract.write_text("foobar", encoding="utf8") settings = {"mock": {"foo": "bar"}} _ = compilers.registered_compilers # Ensures cached property is set. diff --git a/tests/functional/test_config.py b/tests/functional/test_config.py index 1e9e76111c..31041c281f 100644 --- a/tests/functional/test_config.py +++ b/tests/functional/test_config.py @@ -8,6 +8,7 @@ from ape.exceptions import ConfigError from ape.managers.config import CONFIG_FILE_NAME, merge_configs from ape.types import GasLimit +from ape.utils import create_tempdir from ape_ethereum.ecosystem import EthereumConfig, NetworkConfig from ape_networks import CustomNetwork from tests.functional.conftest import PROJECT_WITH_LONG_CONTRACTS_FOLDER @@ -219,7 +220,7 @@ def test_global_config(data_folder, config): test: number_of_accounts: 11 """.strip() - config_file.write_text(config_content) + config_file.write_text(config_content, encoding="utf8") global_config = config.load_global_config() assert global_config.get_config("test").number_of_accounts == 11 config_file.unlink(missing_ok=True) @@ -346,3 +347,24 @@ def hacked_in_method(): finally: config.local_project.config._get_config_plugin_classes = original_method + + +def test_write_to_disk_json(config): + with create_tempdir() as base_path: + path = base_path / "config.json" + config.write_to_disk(path) + assert path.is_file() + + +def test_write_to_disk_yaml(config): + with create_tempdir() as base_path: + path = base_path / "config.yaml" + config.write_to_disk(path) + assert path.is_file() + + +def test_write_to_disk_txt(config): + with create_tempdir() as base_path: + path = base_path / "config.txt" + with pytest.raises(ConfigError, match=f"Unsupported destination file type '{path}'."): + config.write_to_disk(path) diff --git a/tests/functional/test_coverage.py b/tests/functional/test_coverage.py index 00a3fcdd18..bba0684227 100644 --- a/tests/functional/test_coverage.py +++ b/tests/functional/test_coverage.py @@ -247,7 +247,7 @@ def init_profile(source_cov, src): # Create a source file. file = tmp.path / "contracts" / filename file.parent.mkdir(exist_ok=True, parents=True) - file.write_text("testing") + file.write_text("testing", encoding="utf8") # Ensure the TB refers to this source. tb_data["source_path"] = f"{tmp.path}/contracts/{filename}" diff --git a/tests/functional/test_dependencies.py b/tests/functional/test_dependencies.py index c5f51e8628..80c0d08e70 100644 --- a/tests/functional/test_dependencies.py +++ b/tests/functional/test_dependencies.py @@ -40,14 +40,14 @@ def project_with_downloaded_dependencies(project, oz_dependencies_config): manifest_source = oz_manifests / version / "openzeppelin.json" manifest_dest = oz_manifests_dest / f"{version.replace('.', '_')}.json" manifest_dest.unlink(missing_ok=True) - manifest_dest.write_text(manifest_source.read_text()) + manifest_dest.write_text(manifest_source.read_text(), encoding="utf8") # Also, copy in the API data api_dest = base.api_folder / package_id / f"{version.replace('.', '_')}.json" api_dest.unlink(missing_ok=True) cfg = [x for x in oz_dependencies_config["dependencies"] if x["version"] == version][0] api_dest.parent.mkdir(exist_ok=True, parents=True) - api_dest.write_text(json.dumps(cfg)) + api_dest.write_text(json.dumps(cfg), encoding="utf8") with project.temp_config(**oz_dependencies_config): yield project @@ -125,7 +125,7 @@ def test_decode_dependency_with_config_override(project): base_path = tmp_project.path / path contracts_path = base_path / "contracts" contracts_path.mkdir(parents=True, exist_ok=True) - (contracts_path / "contract.json").write_text('{"abi": []}') + (contracts_path / "contract.json").write_text('{"abi": []}', encoding="utf8") data = {"name": "FooBar", "local": path, "config_override": settings} dependency = tmp_project.dependencies.decode_dependency(**data) @@ -186,7 +186,7 @@ def test_add(project): with project.isolate_in_tempdir() as tmp_project: contracts_path = tmp_project.path / "src" contracts_path.mkdir(exist_ok=True, parents=True) - (contracts_path / "contract.json").write_text('{"abi": []}') + (contracts_path / "contract.json").write_text('{"abi": []}', encoding="utf8") data = {"name": "FooBar", "local": f"{tmp_project.path}"} dependency = project.dependencies.add(data) @@ -217,7 +217,7 @@ def test_install(project, mocker): with project.isolate_in_tempdir() as tmp_project: contracts_path = tmp_project.path / "src" contracts_path.mkdir(exist_ok=True, parents=True) - (contracts_path / "contract.json").write_text('{"abi": []}') + (contracts_path / "contract.json").write_text('{"abi": []}', encoding="utf8") data = {"name": "FooBar", "local": f"{tmp_project.path}"} get_spec_spy = mocker.spy(tmp_project.dependencies, "_get_specified") install_dep_spy = mocker.spy(tmp_project.dependencies, "install_dependency") @@ -369,10 +369,10 @@ def node_modules_path(self, project_with_npm_dependency, request, mock_home_dire contracts_folder = package_folder / "contracts" contracts_folder.mkdir(parents=True) package_json = package_folder / "package.json" - package_json.write_text(f'{{"version": "{self.VERSION}"}}') + package_json.write_text(f'{{"version": "{self.VERSION}"}}', encoding="utf8") file = contracts_folder / "contract.json" source_content = '{"abi": []}' - file.write_text(source_content) + file.write_text(source_content, encoding="utf8") yield base def test_fetch(self, node_modules_path, project_with_npm_dependency): @@ -582,7 +582,7 @@ def test_load_contracts_with_config_override(self, project): override = {"contracts_folder": "src"} contracts_path = tmp_project.path / "src" contracts_path.mkdir(exist_ok=True, parents=True) - (contracts_path / "contract.json").write_text('{"abi": []}') + (contracts_path / "contract.json").write_text('{"abi": []}', encoding="utf8") # use_cache=False only to lessen stateful test clobbering. data = { diff --git a/tests/functional/test_project.py b/tests/functional/test_project.py index de42887727..79ae7d44f8 100644 --- a/tests/functional/test_project.py +++ b/tests/functional/test_project.py @@ -132,7 +132,7 @@ def test_contracts_folder_deduced(tmp_project): contracts_folder.mkdir() contract = contracts_folder / "tryme.json" abi = [{"name": "foo", "type": "fallback", "stateMutability": "nonpayable"}] - contract.write_text(json.dumps(abi)) + contract.write_text(json.dumps(abi), encoding="utf8") new_project = Project(new_project_path) actual = new_project.contracts_folder assert actual == contracts_folder @@ -180,7 +180,7 @@ def test_getattr_detects_changes(tmp_project): path = tmp_project.sources.lookup(source_id) assert path path.unlink(missing_ok=True) - path.write_text(content) + path.write_text(content, encoding="utf8") # Should have re-compiled. contract = tmp_project.Other assert "retrieve" in contract.contract_type.methods @@ -193,7 +193,7 @@ def test_getattr_empty_contract(tmp_project): source_id = tmp_project.Other.contract_type.source_id path = tmp_project.sources.lookup(source_id) path.unlink(missing_ok=True) - path.write_text("") + path.write_text("", encoding="utf8") # Should have re-compiled. contract = tmp_project.Other assert not contract.contract_type.methods @@ -289,8 +289,8 @@ def test_extract_manifest_excludes_cache(tmp_project): cachefile = tmp_project.contracts_folder / ".cache" / "CacheFile.json" cachefile2 = tmp_project.contracts_folder / ".cache" / "subdir" / "Cache2.json" cachefile2.parent.mkdir(parents=True) - cachefile.write_text("Doesn't matter") - cachefile2.write_text("Doesn't matter") + cachefile.write_text("Doesn't matter", encoding="utf8") + cachefile2.write_text("Doesn't matter", encoding="utf8") manifest = tmp_project.extract_manifest() assert isinstance(manifest, PackageManifest) assert ".cache/CacheFile.json" not in (manifest.sources or {}) @@ -355,7 +355,7 @@ def test_load_contracts_detect_change(tmp_project, ape_caplog): new_content = content.replace("foo", "bar") assert "bar" in new_content, "Test setup failed. Unexpected file content." path.unlink() - path.write_text(new_content) + path.write_text(new_content, encoding="utf8") # Prove re-compiles. contracts = tmp_project.load_contracts() @@ -375,7 +375,7 @@ def test_load_contracts_after_deleting_same_named_contract(tmp_project, compiler collision error from deleted files. """ init_contract = tmp_project.contracts_folder / "foo.__mock__" - init_contract.write_text("LALA") + init_contract.write_text("LALA", encoding="utf8") compilers.registered_compilers[".__mock__"] = mock_compiler result = tmp_project.load_contracts() assert "foo" in result @@ -390,7 +390,7 @@ def test_load_contracts_after_deleting_same_named_contract(tmp_project, compiler # Create a new contract with the same name. new_contract = tmp_project.contracts_folder / "bar.__mock__" - new_contract.write_text("BAZ") + new_contract.write_text("BAZ", encoding="utf8") mock_compiler.overrides = {"contractName": "foo"} result = tmp_project.load_contracts() assert "foo" in result @@ -525,10 +525,10 @@ def test_project_api_foundry_and_ape_config_found(foundry_toml): """ with ape.Project.create_temporary_project() as temp_project: foundry_cfg_file = temp_project.path / "foundry.toml" - foundry_cfg_file.write_text(foundry_toml) + foundry_cfg_file.write_text(foundry_toml, encoding="utf8") ape_cfg_file = temp_project.path / "ape-config.yaml" - ape_cfg_file.write_text("name: testfootestfootestfoo") + ape_cfg_file.write_text("name: testfootestfootestfoo", encoding="utf8") actual = temp_project.project_api assert isinstance(actual, ApeProject) @@ -658,9 +658,9 @@ def gitmodules(self): def test_extract_config(self, foundry_toml, gitmodules, mock_github): with ape.Project.create_temporary_project() as temp_project: cfg_file = temp_project.path / "foundry.toml" - cfg_file.write_text(foundry_toml) + cfg_file.write_text(foundry_toml, encoding="utf8") gitmodules_file = temp_project.path / ".gitmodules" - gitmodules_file.write_text(gitmodules) + gitmodules_file.write_text(gitmodules, encoding="utf8") api = temp_project.project_api mock_github.get_repo.return_value = {"default_branch": "main"} @@ -736,7 +736,7 @@ def clean(): # Duplicate contract so that there are multiple with the same name. for nested_src in (nested_source_a, nested_source_b): nested_src.touch() - nested_src.write_text(source_path.read_text()) + nested_src.write_text(source_path.read_text(), encoding="utf8") # Top-level match. for base in (source_path, str(source_path), "Contract", "Contract.json"): @@ -793,7 +793,7 @@ def test_paths_exclude(self, tmp_project): cache = tmp_project.contracts_folder / ".cache" cache.mkdir(exist_ok=True) random_file = cache / "dontmindme.json" - random_file.write_text("what, this isn't json?!") + random_file.write_text("what, this isn't json?!", encoding="utf8") path_ids = { f"{tmp_project.contracts_folder.name}/{src.name}" diff --git a/tests/integration/cli/test_accounts.py b/tests/integration/cli/test_accounts.py index 1c6da566d0..8a565cf1fb 100644 --- a/tests/integration/cli/test_accounts.py +++ b/tests/integration/cli/test_accounts.py @@ -38,7 +38,7 @@ def temp_keyfile_path(config): @pytest.fixture def temp_keyfile(temp_keyfile_path, keyparams): - temp_keyfile_path.write_text(json.dumps(keyparams)) + temp_keyfile_path.write_text(json.dumps(keyparams), encoding="utf8") yield temp_keyfile_path diff --git a/tests/integration/cli/test_compile.py b/tests/integration/cli/test_compile.py index c7822959a5..55897c8572 100644 --- a/tests/integration/cli/test_compile.py +++ b/tests/integration/cli/test_compile.py @@ -126,7 +126,7 @@ def test_compile_when_sources_change(ape_cli, runner, integ_project, clean_cache modified_source_text = source_path.read_text().replace("bar", "foo") source_path.unlink() source_path.touch() - source_path.write_text(modified_source_text) + source_path.write_text(modified_source_text, encoding="utf8") result = runner.invoke( ape_cli, ("compile", "--project", f"{integ_project.path}"), catch_exceptions=False ) @@ -160,7 +160,7 @@ def clean(): temp_dir.mkdir() try: source_copy.touch() - source_copy.write_text(source_path.read_text()) + source_copy.write_text(source_path.read_text(), encoding="utf8") result = runner.invoke( ape_cli, ("compile", "--project", f"{integ_project.path}"), catch_exceptions=False ) @@ -190,7 +190,7 @@ def test_compile_when_source_contains_return_characters( modified_source_text = f"{source_path.read_text()}\r" source_path.unlink() source_path.touch() - source_path.write_text(modified_source_text) + source_path.write_text(modified_source_text, encoding="utf8") arguments = ("compile", "--project", f"{integ_project.path}") result = runner.invoke(ape_cli, arguments, catch_exceptions=False) assert result.exit_code == 0, result.output diff --git a/tests/integration/cli/test_console.py b/tests/integration/cli/test_console.py index 26371c6e92..95df40ce5f 100644 --- a/tests/integration/cli/test_console.py +++ b/tests/integration/cli/test_console.py @@ -62,7 +62,7 @@ def no_console_error(result): def write_ape_console_extras(base_path, contents): extras_file = base_path.joinpath("ape_console_extras.py") - extras_file.write_text(contents) + extras_file.write_text(contents, encoding="utf8") return extras_file diff --git a/tests/integration/cli/test_run.py b/tests/integration/cli/test_run.py index 6f9db5836a..19b58609bc 100644 --- a/tests/integration/cli/test_run.py +++ b/tests/integration/cli/test_run.py @@ -206,7 +206,7 @@ def test_run_recompiles_if_needed(runner, ape_cli, scripts_runner, integ_project method_name = integ_project.VyperContract.contract_type.view_methods[0].name new_method_name = f"f__{method_name}__" new_contract_text = contract.read_text().replace(method_name, new_method_name) - contract.write_text(new_contract_text) + contract.write_text(new_contract_text, encoding="utf8") # Run the script. It better recompile first! result = scripts_runner.invoke("output_contract_view_methods")