Proof of concept of fixtures as describe block funcargs #39
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I mentioned in #38 that I'd send a WIP PR if I managed to find some time, but of course I forgot. I had some time to do some hacking this evening, and I eventually arrived at a version that sort of achieves what I had hoped, so here it is. It's far from complete, there are numerous test failures and TODOs, in fact, but it's a start.
To summarise, what I did is capture the describe block's arguments using
inspect
and calling it with dummy arguments which store the argument name to gather the local definitions. When those dummy arguments are used in a test spec, they're added to the spec's closure, but we need the name as that would be the only way to match fixture values to the closure cells. Then I wrap each spec inside of a new function which loads the fixture values and either passes them as a normal argument, or temporarily modifies the closure to inject it. I've also added a sort of defence mechanism to prevent dereferencing the dummy arguments in the describe block itself, as that would lead to unexpected behaviour. This is done by scanning the function's bytecode for certain opcodes that load those arguments, but it's not thoroughly tested yet and likely not 100% reliable.The main TODOs:
@pytest.mark.parametrize
, as that scans the test spec's params to check if it actually uses the parametrized variable. However, we're returning the wrapper, which doesn't include those params, so that fails. This could be fixed with some more meta-hacking or hooking more into pytest itself. hypothesis faced a similar problem and solved it by dynamically defining a new function with the adapted signature usingexec
.@behaves_like
describe block. I don't want to rule out that possibility, but that could easily get very messy very quickly.Smaller TODOs:
All in all, I'm of the opinion that this is all some very cool and interesting meta-hacking, but I'm concerned as to whether it's worthwhile continuing this endeavour. This buggy implementation already doubled the size of the plugin's main code, and I'm expecting it to grow significantly more to account for some of the TODOs. All that to prevent an error which is trivial to fix, and becomes a non-issue very quickly. It's certainly flashy and a cool example of dependency injection, but I'm not sure that the substantial increase in complexity and future maintenance efforts would be worth it.
Unless there's high demand for this or I'm craving another meta-hacking session, it's unlikely that I'll continue work on this any time soon. So if anyone wants to work further on this (or start from scratch), feel free to do so.