-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 mypy error: Too few arguments to "makeTestCaseClasses" #1261
Conversation
|
This helps address some of the mypy errors found here: https://travis-ci.com/github/twisted/twisted/jobs/327861205#L4888 such as: I followed the suggestions here: https://stackoverflow.com/questions/44640479/mypy-annotation-for-classmethod-returning-instance |
1139eb9
to
1efd196
Compare
- Fix some pyflakes/pylint errors. - Add some type annotations to appease mypy
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.
Aside from needing to annotating the type: ignore comments before landing, this looks great, thank you!
| from twisted.internet import process | ||
| except ImportError: | ||
| process = None # type: ignore |
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.
Could you file a ticket for a more mypy-friendly way of doing this, and link it here before landing? I am OK with adding type: ignore for an import hack like this (particularly in a test helper) but before the pattern replicates ad infinitum I would like to record the fact that we should clean it up somehow later.
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.
There already is an open issue python/mypy#1297 (comment) where this use-case was mentioned. I'll follow up there, and see what I can find out.
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.
So based on this: python/mypy#1297 (comment)
I think we can remove the #type: ignore, and do this:
try
from twisted.internet import process as _process
except ImportError:
process = None
else:
process = _process
| def makeTestCaseClasses(cls): | ||
| @classmethod | ||
| def makeTestCaseClasses( | ||
| cls: Type['ReactorBuilder'] |
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.
Why are these all quoted? The names are imported, so they should be in scope?
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.
ReactorBuilder is defined in the same file, so I had to use the syntax Class name forward references, as described here: https://mypy.readthedocs.io/en/stable/kinds_of_types.html#class-name-forward-references
otherwise mypy would complain.
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.
Actually mypy did not complain. If I didn't put ReactorBuilder in quotes, pyflakes complained with:
src/twisted/internet/test/reactormixins.py:331:19 undefined name 'ReactorBuilder'
src/twisted/internet/test/reactormixins.py:332:31 undefined name 'ReactorBuilder'
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 changed this so SynchronousTestCase is not in quotes, but I still need 'ReactorBuilder` to be in quotes for the annotation.
| """ | ||
| Create a L{SynchronousTestCase} subclass which mixes in C{cls} for each | ||
| known reactor and return a dict mapping their names to them. | ||
| """ | ||
| classes = {} | ||
| classes = {} # type: Dict[str, Union[Type['ReactorBuilder'], Type['SynchronousTestCase']]] # noqa |
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.
Quoting in type comments seems doubly pointless, even if it works?
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'm not sure exactly how mypy parses the file and comments to do all the type checks. I just kept the use the same to be consistent.
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.
For mypy's sake you never need to quote, but the Python VM does require it when using a type name before it is defined, unless you use from __future__ import annotations, but that's a Python 3.7 feature.
Of course the Python VM doesn't process the contents of comments, so no quoting is necessary there.
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.
@mthuurne Yes, for now I need to quote 'ReactorBuilder'. Since Twisted still supports Python 3.5 and 3.6, I cannot use from __future__ import annotations
| for reactor in cls._reactors: | ||
| shortReactorName = reactor.split(".")[-1] | ||
| name = (cls.__name__ + "." + shortReactorName + "Tests").replace(".", "_") | ||
| class testcase(cls, SynchronousTestCase): | ||
| name = (cls.__name__ + "." + shortReactorName + |
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 guess twistedchecker complained at you?
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.
Yes, I made a lot of small changes like this to appease twistedchecker in terms of line spacing, line length, etc.
| name = (cls.__name__ + "." + shortReactorName + | ||
| "Tests").replace(".", "_") | ||
|
|
||
| class testcase(cls, SynchronousTestCase): # type: ignore |
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.
What doesn't mypy like about this? Again, I would like type: ignores to be used sparingly and we should generally have a plan for how to get rid of them, at least file an upstream bug against mypy.
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.
If I didn't put that #type: ignore there, mypy would give:
src/twisted/internet/test/reactormixins.py:343:28: error: Variable "cls" is not valid as a type [valid-type]
class testcase(cls, SynchronousTestCase):
^
src/twisted/internet/test/reactormixins.py:343:28: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
src/twisted/internet/test/reactormixins.py:343:28: error: Invalid base class "cls" [misc]
class testcase(cls, SynchronousTestCase):
The reason for this is that cls is a parameter, and the type of the parameter is only known at runtime. So mypy doesn't know what type it is, and can't check it.
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.
Note that if you have to use type: ignore, you can specify the error category to ignore, which reduces the chance of accidentally silencing unexpected errors. In this case it would be # type: ignore[valid-type].
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.
@mthuurne thanks for the feedback. I've changed this line to specifically turn off two mypy errors on this line:
class testcase(cls, SynchronousTestCase): # type: ignore[valid-type,misc]
| __module__ = cls.__module__ | ||
| if reactor in cls.skippedReactors: | ||
| skip = cls.skippedReactors[reactor] | ||
| try: | ||
| reactorFactory = namedAny(reactor) | ||
| except: | ||
| except Exception: |
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.
This should be BaseException to avoid a change to its semantics.
https://twistedmatrix.com/trac/ticket/9811