-
-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: resetting state modifying fixtures not function scoped #654
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small formatting issues, but this works great and I am glad it was an easy fix. Thank you for contributing!
615a9c1
to
0c91a2c
Compare
@fubuloubu @unparalleled-js I guess the next question is, beyond function scope isolation, do you think module level isolation should be implemented here as well? It may lead to needing isolation by default for all scopes 🤔 |
At least supporting "module" and "function" scopes would be nice. "session" scopes should be left only for accounts basically, but supporting them more generally is important. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! Verifying the action of this change locally before merging
0b216be
to
5e32047
Compare
@fubuloubu Actually 🤔 second guessing myself here but is snapshotting right before the test is ran ok? Using brownie for example, the function isolation fixture runs first at the function level: SETUP F function_isolation (fixtures used: chain, history, module_isolation)
SETUP F alice_unlock_time (fixtures used: chain)
SETUP F lock_alice (fixtures used: alice, alice_lock_value, alice_unlock_time, crv, vecrv)
SETUP F timedelta_bps[60]
tests/boosts/test_batch_cancel_boosts.py::test_third_parties_can_only_cancel_past_expiry[60] (fixtures used: VotingEscrowDelegation, accounts, alice, alice_lock_value, alice_unlock_time, bob, chain, charlie, crv, function_isolation, history, lock_alice, module_isolation, multicall, pm, timedelta_bps, veboost, vecrv)
TEARDOWN F timedelta_bps[60]
TEARDOWN F lock_alice
TEARDOWN F alice_unlock_time
TEARDOWN F function_isolation But this PR would change it to having snapshotting right before the test is ran, in brownie that would look like the isolation fixture is run last Edit: The effect is, if a enduser does any state changes in a function level fixture, those will not be reset in the development chain. i.e. if I deploy |
This seems like desired behavior. If you want to snapshot things after initial setup, use Edit: I know Brownie works differently, but I want this behavior so that we hook more deeply into how pytest already works, so that there is no context switching between ape-flavored pytest fixtures and regular pytest fixtures |
Was inspecting this some more (i.e. snapshotting by default around scopes), and my initial idea was to obviously find whatever hook pytest has for setting up and tearing down fixtures (which it does). The downside is, the way fixture setup is done in pytest, a single call is done to the hook for each test (as opposed to a call to the hook per fixture): https://github.com/pytest-dev/pytest/blob/main/src/_pytest/fixtures.py#L1096-L1118 So off the top of my head I'm thinking as a dirty tactic, we could re-implement the function body in the Ape runner, overriding pytest's default, and then do the snapshot and resetting within it. cc: @fubuloubu @unparalleled-js Edit: there's also the option of fixtures, but must concur, snapshotting by default is most reasonable Looks like maybe internal fixtures is the way to go (whatever that may mean) |
Would |
Hmm, those hooks seem to be too early. I think maybe using pytest_collection_modifyitems may be what we're looking for? Along with some internal fixtures ( That and I guess adding a command line option The end result is, built-in isolation that users don't even have to worry about requesting, + an option to disable it if needed (for users that may want to roll their own or whatever). And since the fixture names start with an underscore, they're not shown in the |
5e32047
to
745919a
Compare
Ended up going for internal fixtures + modifying the setup phase to reorder the execution order of the fixtures. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the best of both worlds approach. Generally, I prefer truthy-flags (e.g. --enable-isolation) though I think this makes sense in this case.
src/ape/pytest/fixtures.py
Outdated
self.chain_manager.restore(snapshot_id) | ||
|
||
@pytest.fixture(scope="package") | ||
def _package_isolation(self, _session_isolation) -> Iterator[None]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be called "_session_isolation" to work? (Looking at the logic in the runners for dynamically setting the fixture)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah the leading underscore you mean? That's just so it's considered internal, and not shown to the end user via pytest --fixtures
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a really interesting approach. This approval is for the approach, I still would like to try it locally and address @unparalleled-js's comments before merging.
Co-authored-by: El De-dog-lo <3859395+fubuloubu@users.noreply.github.com>
Co-authored-by: El De-dog-lo <3859395+fubuloubu@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, this is awesome
from ape.managers.chain import ChainManager | ||
from ape.managers.project import ProjectManager | ||
from ape.utils import ManagerAccessMixin | ||
|
||
|
||
class PytestApeFixtures(ManagerAccessMixin): | ||
def __init__(self): | ||
self._warned_for_unimplemented_snapshot = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first I thought this said "warn for unimplemented snapshot", would flipping the parity of this make it easier to read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i've no clue lol, whatever seems best I'll rename it as (i've got no attachment), this name I just copy and pasted from the runner.py
file
Tested with https://github.com/yearn/veyfi (w/ upgrading most everything to session fixtures), and got this error: $ ape test
=========================================================================================== test session starts ============================================================================================
platform linux -- Python 3.9.9, pytest-6.2.5, py-1.11.0, pluggy-0.13.1
rootdir: Yearn/v3/veYFI
plugins: eth-ape-0.1.5.dev33+g37efd453, web3-5.28.0
collected 45 items
INFO: Starting 'Hardhat node' process.
tests/functional/test_extra_rewards.py ...... [ 13%]
tests/functional/test_gauge.py ......... [ 33%]
tests/functional/test_gauge_reward_distribution.py ......... [ 53%]
tests/functional/test_ve_yfi_rewards.py ....... [ 68%]
tests/functional/test_vote_delegation.py ...E [ 77%]
tests/functional/test_voting_escrow.py .......... [100%]
INFO: Stopping 'Hardhat node' process.
================================================================================================== ERRORS ==================================================================================================
___________________________________________________________________________ ERROR at setup of test_many_delegates_gas_usage[100] ___________________________________________________________________________
.0 = <list_iterator object at 0x7f069b209df0>
> scopes = [item._fixtureinfo.name2fixturedefs[f][0].scope for f in item.fixturenames]
E KeyError: '_session_isolation'
site-packages/ape/pytest/runners.py:96: KeyError
========================================================================================= short test summary info ==========================================================================================
ERROR tests/functional/test_vote_delegation.py::test_many_delegates_gas_usage[100] - KeyError: '_session_isolation' |
ahh that was a nice catch, fixed in 4c9f87e |
489751f
to
4c9f87e
Compare
What I did
Remove chain snapshotting prior to fixture execution/setup, and create a ape_test fixture
function_isolation
which does the behavior presumably desired (snapshot prior to tests and reset after).fixes: #653
fixes: APE-165
How I did it
Simple, copy the original
pytest_runtest_protocol
function body and paste it into thefunction_isolation
fixture.How to verify it
Check the added test case, also can be verified against the test project in issue #653
Checklist