From 3b746bba2792dcccd001f81263bd9c46f83a4668 Mon Sep 17 00:00:00 2001
From: Juliya Smith <jules@apeworx.io>
Date: Thu, 31 Oct 2024 14:13:31 -0500
Subject: [PATCH 1/4] fix: use add opt

---
 src/ape/pytest/plugin.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/ape/pytest/plugin.py b/src/ape/pytest/plugin.py
index 72d09c1809..2a953e11b1 100644
--- a/src/ape/pytest/plugin.py
+++ b/src/ape/pytest/plugin.py
@@ -64,12 +64,13 @@ def add_option(*names, **kwargs):
         action="store",
         help="A comma-separated list of contract:method-name glob-patterns to ignore.",
     )
-    parser.addoption("--coverage", action="store_true", help="Collect contract coverage.")
+    add_option("--coverage", action="store_true", help="Collect contract coverage.")
 
     # NOTE: Other pytest plugins, such as hypothesis, should integrate with pytest separately
 
 
 def pytest_configure(config):
+    breakpoint()
     # Do not include ape internals in tracebacks unless explicitly asked
     if not config.getoption("--show-internal"):
         if path_str := sys.modules["ape"].__file__:

From de129c53d1aadfb00f277809b171eca4dcfdbd78 Mon Sep 17 00:00:00 2001
From: Juliya Smith <jules@apeworx.io>
Date: Thu, 31 Oct 2024 14:44:06 -0500
Subject: [PATCH 2/4] perf: avoid settin up runner during help

---
 src/ape/pytest/config.py          | 13 +++++++++----
 src/ape/pytest/contextmanagers.py |  4 +++-
 src/ape/pytest/coverage.py        | 12 +++++++-----
 src/ape/pytest/gas.py             |  5 ++---
 src/ape/pytest/plugin.py          | 23 +++++++----------------
 src/ape/pytest/runners.py         |  4 +++-
 6 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/src/ape/pytest/config.py b/src/ape/pytest/config.py
index 77825f338f..a60cf15fd9 100644
--- a/src/ape/pytest/config.py
+++ b/src/ape/pytest/config.py
@@ -1,14 +1,17 @@
 from functools import cached_property
 from typing import TYPE_CHECKING, Any, Optional, Union
 
-from ape.types.trace import ContractFunctionPath
 from ape.utils.basemodel import ManagerAccessMixin
 
 if TYPE_CHECKING:
     from _pytest.config import Config as PytestConfig
 
+    from ape.types.trace import ContractFunctionPath
+
+
+def _get_config_exclusions(config) -> list["ContractFunctionPath"]:
+    from ape.types.trace import ContractFunctionPath
 
-def _get_config_exclusions(config) -> list[ContractFunctionPath]:
     return [
         ContractFunctionPath(contract_name=x.contract_name, method_name=x.method_name)
         for x in config.exclude
@@ -74,10 +77,12 @@ def show_internal(self) -> bool:
         return self.pytest_config.getoption("--show-internal")
 
     @cached_property
-    def gas_exclusions(self) -> list[ContractFunctionPath]:
+    def gas_exclusions(self) -> list["ContractFunctionPath"]:
         """
         The combination of both CLI values and config values.
         """
+        from ape.types.trace import ContractFunctionPath
+
         cli_value = self.pytest_config.getoption("--gas-exclude")
         exclusions = (
             [ContractFunctionPath.from_str(item) for item in cli_value.split(",")]
@@ -89,7 +94,7 @@ def gas_exclusions(self) -> list[ContractFunctionPath]:
         return exclusions
 
     @cached_property
-    def coverage_exclusions(self) -> list[ContractFunctionPath]:
+    def coverage_exclusions(self) -> list["ContractFunctionPath"]:
         return _get_config_exclusions(self.ape_test_config.coverage)
 
     def get_pytest_plugin(self, name: str) -> Optional[Any]:
diff --git a/src/ape/pytest/contextmanagers.py b/src/ape/pytest/contextmanagers.py
index 652ce4ba92..905d1baebb 100644
--- a/src/ape/pytest/contextmanagers.py
+++ b/src/ape/pytest/contextmanagers.py
@@ -4,7 +4,6 @@
 
 from ethpm_types.abi import ErrorABI
 
-from ape.contracts import ContractInstance
 from ape.exceptions import ContractLogicError, CustomError, TransactionError
 from ape.utils.basemodel import ManagerAccessMixin
 
@@ -105,6 +104,9 @@ def _check_expected_message(self, exception: ContractLogicError):
             raise AssertionError(f"{assertion_error_prefix} but got '{actual}'.")
 
     def _check_custom_error(self, exception: Union[CustomError]):
+        # perf: avoid loading from contracts namespace until needed.
+        from ape.contracts import ContractInstance
+
         expected_error_cls = self.expected_message
 
         if not isinstance(expected_error_cls, ErrorABI) and not isinstance(
diff --git a/src/ape/pytest/coverage.py b/src/ape/pytest/coverage.py
index 784a025f06..768c385492 100644
--- a/src/ape/pytest/coverage.py
+++ b/src/ape/pytest/coverage.py
@@ -5,7 +5,6 @@
 import click
 
 from ape.logging import logger
-from ape.types.coverage import CoverageProject, CoverageReport
 from ape.utils.basemodel import ManagerAccessMixin
 from ape.utils.misc import get_current_timestamp_ms
 from ape.utils.os import get_full_extension, get_relative_path
@@ -17,6 +16,7 @@
 
     from ape.managers.project import ProjectManager
     from ape.pytest.config import ConfigWrapper
+    from ape.types.coverage import CoverageReport
     from ape.types.trace import ContractFunctionPath, ControlFlow, SourceTraceback
 
 
@@ -30,7 +30,7 @@ def __init__(
         self._sources: Union[
             Iterable["ContractSource"], Callable[[], Iterable["ContractSource"]]
         ] = sources
-        self._report: Optional[CoverageReport] = None
+        self._report: Optional["CoverageReport"] = None
 
     @property
     def sources(self) -> list["ContractSource"]:
@@ -45,7 +45,7 @@ def sources(self) -> list["ContractSource"]:
         return self._sources
 
     @property
-    def report(self) -> CoverageReport:
+    def report(self) -> "CoverageReport":
         if self._report is None:
             self._report = self._init_coverage_profile()
 
@@ -57,7 +57,9 @@ def reset(self):
 
     def _init_coverage_profile(
         self,
-    ) -> CoverageReport:
+    ) -> "CoverageReport":
+        from ape.types.coverage import CoverageProject, CoverageReport
+
         # source_id -> pc(s) -> times hit
         project_coverage = CoverageProject(name=self.project.name or "__local__")
 
@@ -161,7 +163,7 @@ def __init__(
 
     @property
     def data(self) -> Optional[CoverageData]:
-        if not self.config_wrapper.track_coverage:
+        if not self.enabled:
             return None
 
         elif self._data is None:
diff --git a/src/ape/pytest/gas.py b/src/ape/pytest/gas.py
index 1f37af2b68..c9739717b3 100644
--- a/src/ape/pytest/gas.py
+++ b/src/ape/pytest/gas.py
@@ -2,7 +2,6 @@
 
 from evm_trace.gas import merge_reports
 
-from ape.types.trace import GasReport
 from ape.utils.basemodel import ManagerAccessMixin
 from ape.utils.trace import _exclude_gas, parse_gas_table
 
@@ -13,7 +12,7 @@
     from ape.api.trace import TraceAPI
     from ape.pytest.config import ConfigWrapper
     from ape.types.address import AddressType
-    from ape.types.trace import ContractFunctionPath
+    from ape.types.trace import ContractFunctionPath, GasReport
 
 
 class GasTracker(ManagerAccessMixin):
@@ -24,7 +23,7 @@ class GasTracker(ManagerAccessMixin):
 
     def __init__(self, config_wrapper: "ConfigWrapper"):
         self.config_wrapper = config_wrapper
-        self.session_gas_report: Optional[GasReport] = None
+        self.session_gas_report: Optional["GasReport"] = None
 
     @property
     def enabled(self) -> bool:
diff --git a/src/ape/pytest/plugin.py b/src/ape/pytest/plugin.py
index 2a953e11b1..31330260d7 100644
--- a/src/ape/pytest/plugin.py
+++ b/src/ape/pytest/plugin.py
@@ -1,6 +1,5 @@
 import sys
 from pathlib import Path
-from typing import TYPE_CHECKING, Optional
 
 from ape.exceptions import ConfigError
 from ape.pytest.config import ConfigWrapper
@@ -10,16 +9,6 @@
 from ape.pytest.runners import PytestApeRunner
 from ape.utils.basemodel import ManagerAccessMixin
 
-if TYPE_CHECKING:
-    from ape.api.networks import EcosystemAPI
-
-
-def _get_default_network(ecosystem: Optional["EcosystemAPI"] = None) -> str:
-    if ecosystem is None:
-        ecosystem = ManagerAccessMixin.network_manager.default_ecosystem
-
-    return ecosystem.name
-
 
 def pytest_addoption(parser):
     def add_option(*names, **kwargs):
@@ -40,7 +29,6 @@ def add_option(*names, **kwargs):
     add_option(
         "--network",
         action="store",
-        default=_get_default_network(),
         help="Override the default network and provider (see ``ape networks list`` for options).",
     )
     add_option(
@@ -70,7 +58,6 @@ def add_option(*names, **kwargs):
 
 
 def pytest_configure(config):
-    breakpoint()
     # Do not include ape internals in tracebacks unless explicitly asked
     if not config.getoption("--show-internal"):
         if path_str := sys.modules["ape"].__file__:
@@ -88,16 +75,20 @@ def is_module(v):
                     pass
 
     config_wrapper = ConfigWrapper(config)
-    receipt_capture = ReceiptCapture(config_wrapper)
-    gas_tracker = GasTracker(config_wrapper)
-    coverage_tracker = CoverageTracker(config_wrapper)
 
     if not config.option.verbose:
         # Enable verbose output if stdout capture is disabled
         config.option.verbose = config.getoption("capture") == "no"
     # else: user has already changes verbosity to an equal or higher level; avoid downgrading.
 
+    if "--help" in config.invocation_params.args:
+        # perf: Don't bother setting up running if only showing help.
+        return
+
     # Register the custom Ape test runner
+    receipt_capture = ReceiptCapture(config_wrapper)
+    gas_tracker = GasTracker(config_wrapper)
+    coverage_tracker = CoverageTracker(config_wrapper)
     runner = PytestApeRunner(config_wrapper, receipt_capture, gas_tracker, coverage_tracker)
     config.pluginmanager.register(runner, "ape-test")
 
diff --git a/src/ape/pytest/runners.py b/src/ape/pytest/runners.py
index 6ddac468d8..a6528c53cb 100644
--- a/src/ape/pytest/runners.py
+++ b/src/ape/pytest/runners.py
@@ -9,7 +9,6 @@
 from ape.exceptions import ConfigError
 from ape.logging import LogLevel
 from ape.utils.basemodel import ManagerAccessMixin
-from ape_console._cli import console
 
 if TYPE_CHECKING:
     from ape.api.networks import ProviderContextManager
@@ -90,6 +89,8 @@ def pytest_exception_interact(self, report, call):
                 )
 
         if self.config_wrapper.interactive and report.failed:
+            from ape_console._cli import console
+
             traceback = call.excinfo.traceback[-1]
 
             # Suspend capsys to ignore our own output.
@@ -124,6 +125,7 @@ def pytest_exception_interact(self, report, call):
             click.echo("Starting interactive mode. Type `exit` to halt current test.")
 
             namespace = {"_callinfo": call, **globals_dict, **locals_dict}
+
             console(extra_locals=namespace, project=self.local_project, embed=True)
 
             if capman:

From ea55826bd214f3bae8fec38534bacba2ab6a25df Mon Sep 17 00:00:00 2001
From: Juliya Smith <jules@apeworx.io>
Date: Thu, 31 Oct 2024 14:46:05 -0500
Subject: [PATCH 3/4] perf: avoid imports

---
 src/ape/pytest/plugin.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/ape/pytest/plugin.py b/src/ape/pytest/plugin.py
index 31330260d7..b902444a91 100644
--- a/src/ape/pytest/plugin.py
+++ b/src/ape/pytest/plugin.py
@@ -2,12 +2,6 @@
 from pathlib import Path
 
 from ape.exceptions import ConfigError
-from ape.pytest.config import ConfigWrapper
-from ape.pytest.coverage import CoverageTracker
-from ape.pytest.fixtures import PytestApeFixtures, ReceiptCapture
-from ape.pytest.gas import GasTracker
-from ape.pytest.runners import PytestApeRunner
-from ape.utils.basemodel import ManagerAccessMixin
 
 
 def pytest_addoption(parser):
@@ -74,8 +68,6 @@ def is_module(v):
                 except AttributeError:
                     pass
 
-    config_wrapper = ConfigWrapper(config)
-
     if not config.option.verbose:
         # Enable verbose output if stdout capture is disabled
         config.option.verbose = config.getoption("capture") == "no"
@@ -85,7 +77,15 @@ def is_module(v):
         # perf: Don't bother setting up running if only showing help.
         return
 
+    from ape.pytest.config import ConfigWrapper
+    from ape.pytest.coverage import CoverageTracker
+    from ape.pytest.fixtures import PytestApeFixtures, ReceiptCapture
+    from ape.pytest.gas import GasTracker
+    from ape.pytest.runners import PytestApeRunner
+    from ape.utils.basemodel import ManagerAccessMixin
+
     # Register the custom Ape test runner
+    config_wrapper = ConfigWrapper(config)
     receipt_capture = ReceiptCapture(config_wrapper)
     gas_tracker = GasTracker(config_wrapper)
     coverage_tracker = CoverageTracker(config_wrapper)

From bb2bc142ebb7b387640d70bc4e74ce73c66c1727 Mon Sep 17 00:00:00 2001
From: Juliya Smith <jules@apeworx.io>
Date: Thu, 31 Oct 2024 14:56:31 -0500
Subject: [PATCH 4/4] docs: typo

---
 src/ape/pytest/plugin.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ape/pytest/plugin.py b/src/ape/pytest/plugin.py
index b902444a91..e23dd5bf32 100644
--- a/src/ape/pytest/plugin.py
+++ b/src/ape/pytest/plugin.py
@@ -74,7 +74,7 @@ def is_module(v):
     # else: user has already changes verbosity to an equal or higher level; avoid downgrading.
 
     if "--help" in config.invocation_params.args:
-        # perf: Don't bother setting up running if only showing help.
+        # perf: Don't bother setting up runner if only showing help.
         return
 
     from ape.pytest.config import ConfigWrapper