Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Add hypothesis support #383

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

Conversation

Forty-Bot
Copy link

This PR adds support for hypothesis-style decorations (#253). It's incomplete, for reasons outlined in the commit message.

After playing around with ward a bit while writing this PR, I have concluded that I do not like the UX. I don't plan on using it for any of my projects, so I don't plan on finishing this PR. If anyone else is interested, I think this is really the essential core of what needs to happen.

@Forty-Bot
Copy link
Author

Forty-Bot commented Jun 23, 2024

After playing around with ward a bit while writing this PR, I have concluded that I do not like the UX.

And just to expand on this a bit:

  • The UI is has too many non-functional "pretty" aspects
  • The output on failure is way too verbose. It's very difficult to find the actual error or read the backtrace because so much noise is printed (mostly in the form of the local variables in every backtrace).
  • I really miss pytests's --pdb. And it's impossible to re-implement this easily because of the games ward plays with exceptions.
  • No short command line options for whatever reason (Error: No such option: -h)
  • The house style (all tests named _ and mandatory descriptions in the style of e.g. spec) is grating.

Decorations on functions are not re-applied after rewriting their
assertions. This is because a function like

@foo
def a():
    pass

is (effectively) sugar for

def a():
    pass
a = foo(a)

However, rewrite_assertion only extracts the function from the
recompiled code. To include the decorations, we have to execute the
code and extract the function from the globals. However, this presents a
problem, since the decorator must be in scope when executing the code.
We could use the module's __dict__, but it's possible that the
decorator is redefined between when it is applied to the function and
when the module finishes execution:

def foo(f):
    return f

@foo
def a():
    pass

foo = 5

This will load properly, but would fail if we tried to execute code for
the function with the module's final __dict__. We have similar problems
for constructs like

for i in range(4):
    def _(i=i):
        return i

To really be correct here, we have to rewrite assertions when importing
the module. This is actually much simpler than the existing strategy (as
can be seen by the negative diffstat). It does result in a behavioral
change where all assertions in a test module are rewritten instead of
just those in tests.

This patch does not handle cases like

# a_test.py
import b_test

# b_test.py
assert 1 == 2

because if a_test is imported first, then it will import b_test with the
regular importer and the assertions will not be rewritten. To fix this
correctly, we need to replace the loader to add an exec hook which
applies only to test modules. This patch does not implement this and so
I have marked this patch RFC. However, if such a scenario does not
occur, this is more than enough to get hypothesis working.
@darrenburns
Copy link
Owner

Thanks for the feedback. Ward is incomplete, which is why it's missing many of the things you mentioned.

I'm unsure why you seemed to be expecting feature or UX parity with pytest given the version number.

@Forty-Bot
Copy link
Author

Forty-Bot commented Jun 25, 2024

It's mostly just to record my thoughts on ward. But I don't really consider these features. Half of them are, I assume, aesthetic choices.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants