Skip to content

Move testparse to pytest #3780

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

Merged
merged 2 commits into from
Aug 1, 2017
Merged

Move testparse to pytest #3780

merged 2 commits into from
Aug 1, 2017

Conversation

elazarg
Copy link
Contributor

@elazarg elazarg commented Jul 30, 2017

IIUC, this is an item from #1673. I tried to keep this minimal.

(If this goes well I can add more)

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to work, but I don't know this code very well -- do you understand it well enough to confidently say this is right?

@@ -556,6 +556,9 @@ def repr_failure(self, excinfo: Any) -> str:


class DataSuite:
def __init__(self, *, update_data: bool = False) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're adding this here you should update all the classes that derive from it. Or you can follow the existing pattern and leave it out here but add such a __init__ method to ParserSuite (like all other subclasses of DataSuite do). It seems these classes all need an update_date keyword argument because of the call on line 533 in data.py (line number from master, it's in runtest() in MypyDataCase).

Note that these classes don't seem to be instantiated anywhere, I suppose they are recognized dynamically. Do you know how?

Copy link
Contributor Author

@elazarg elazarg Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added it here since I believe that at the end of the road no class will need it, or any other argument. So for the time being, a prototype __init__ in the base class (just for that keyword) should suffice.

Copy link
Contributor Author

@elazarg elazarg Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These classes are gathered by pytest, passed to as the obj argument to pytest_pycollect_makeitem which returns a MypyDataSuite wrapping this obj. Later the collect method is called on the MypyDataSuite instance, and our implementation iterates over self.obj.cases(), wrapping and yielding the results.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that explanation of how they are called makes sense.

I still think that now you're adding __init__ to DataSuite, all subclasses of it that also define __init__ should be modified to call super().__init__(update_data=update_data). Otherwise my spider-sense about inheritance is unhappy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only class that actually defines a nontrivial __init__ is ASTMergeSuite (and I'm not sure it should, since it does not receive arguments and does not seem to have any mutable state).
May I simply remove the empty __init__ definitions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should remove those __init__ definitions that are now a copy of DataSuite.__init__, and make the others call super().__init__(update_data=update_data). I think there may be one or two that ignore their update_data argument, please figure out if those simply don't need it or if setting it would just be ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the only test that uses this option is testcheck, which uses update_testcase_output if it's set. The others simply ignore it.

parse_files = ['parse.test',
'parse-python2.test']

def cases(self) -> List[DataDrivenTestCase]:
@classmethod
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it actually matter whether this is a class method or an instance method? (I don't know -- can you tell me?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's called without an instance (line 515 in data.py, obj is the class itself); it could have been a static method. Better yet, it could not exist at all, since the needed information here is only the file names and 2-3 options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now it has to be a classmethod since it's this is how it's declared in DataSuite.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I think I realize what confused me. There's a class Suite with a cases() instance method, and a class DataSuite with a cases()` class method. The two are not directly related (though I suspect that some duck typing may still be going on). Anyway, no need to change this back. :-)

@elazarg
Copy link
Contributor Author

elazarg commented Aug 1, 2017

I don't know if that would help the review in any way, but here is an almost-complete migration of everything to pytest, plus making individual tests run in 0.5s instead of 4.

It's "almost", and it's one huge step, and it's outdated. So I'm trying* to do it step by step.
(It was also a couple of months ago so I don't remember all the details).

* (I really am :) )

@elazarg
Copy link
Contributor Author

elazarg commented Aug 1, 2017

Maybe there's a way to automate testing-the-tests to avoid regression. As a sanity check I randomly put errors in the tests and watch them fail. This is not systematic enough though.

@elazarg
Copy link
Contributor Author

elazarg commented Aug 1, 2017

Also, running

pytest -n0 --collect-only

gives a list of collected tests:

...
<Module 'mypy/test/testparse.py'>
  <MypyDataSuite 'ParserSuite'>
    <MypyDataCase 'testEmptyFile'>
    ...
  <MypyDataSuite 'ParseErrorSuite'>
    <MypyDataCase 'testInvalidFunction'>
    ...

@@ -35,9 +35,6 @@


class FineGrainedSuite(DataSuite):
def __init__(self, *, update_data: bool) -> None:
pass

Copy link
Contributor Author

@elazarg elazarg Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or did you want here a call to super().__init__?

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll merge once the tests pass. Thanks for your patience!

@elazarg
Copy link
Contributor Author

elazarg commented Aug 1, 2017

Ok. If everything goes well, please let me know how do you want to continue. There are 3 more data-driven test (testtransform, testtypegen, testsemanal), all of them are similarly straightforward. Do you want a dedicated PR for each of them or a single one?

@gvanrossum gvanrossum merged commit 74725ff into python:master Aug 1, 2017
@gvanrossum
Copy link
Member

Thanks! I much prefer to review smaller pieces (easier to get them done between meetings :-).

@ilinum
Copy link
Collaborator

ilinum commented Aug 3, 2017

Thanks for doing this @elazarg! I will be so happy once we can fully switch to pytest.

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

Successfully merging this pull request may close these issues.

3 participants