-
Notifications
You must be signed in to change notification settings - Fork 161
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
PR: Fix/suppress all mypy complaints (based on master) #674
Changes from all commits
0ab5815
df8224a
bcabc86
f1310de
845786b
7af5370
bcc5cf8
0d13f87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,9 @@ def get_attribute(self, name): | |
raise exceptions.AttributeNotFoundError("Attribute %s not found" % name) | ||
return self.get_attributes()[name] | ||
|
||
def get_module(self): | ||
return None | ||
|
||
def get_type(self): | ||
return self.type | ||
|
||
|
@@ -225,6 +228,9 @@ def get_module(self): | |
current_object = current_object.parent | ||
return current_object | ||
|
||
def get_name(self): | ||
return None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, this method and get_module() aren't supposed to be called, it's just a dummy implementation to satisfy mypy. Maybe it should raise NotImplementedError or something like that instead to indicate that only the child class versions of these methods should actually get called. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's true that Rope probably never calls Imo, however, having to create these two methods indicates a more serious flaw in the Abstract classes. At present, the draft PR #675 implements only step 1 of what might become a multi-step project. This first step merely moves methods from the Step 2 of PR #675 proposes to make Oh what the heck. I may as well say here something that I was planning to save for a discussion. After weeks of studying Rope's code I finally see the following:
Summary Newbies will struggle to understand the relations between Rope's most important classes. PR #664 shows that the class hierarchy can be simplified. If this doesn't happen the Theory of Operation should contain a section about how Rope's classes are related. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Meh. The phrase "inheritance by mro" is a fail. The Aha is that the classes of interest, The existing class declarations are: class BuiltinClass(_BuiltinElement, pyobjects.AbstractClass):
class BuiltinFunction(_BuiltinElement, pyobjects.AbstractFunction):
class BuiltinModule(pyobjects.AbstractModule):
class Generator(pyobjects.AbstractClass):
class Iterator(pyobjects.AbstractClass):
class Lambda(pyobjects.AbstractFunction):
class AbstractClass(PyObject):
class AbstractFunction(PyObject):
class PyObject:
class PyDefinedObject:
class AbstractModule(PyObject):
class PyClass(PyDefinedObject, AbstractClass):
class PyFunction(PyDefinedObject, AbstractFunction):
class _PyModule(PyDefinedObject, AbstractModule): In other words, There is a weird kind of logic in this scheme. It's crazy, but imo it's not crazy enough to be right. PR #664 demonstrates that a simpler (more conventional) scheme is feasible. |
||
|
||
def get_doc(self) -> Optional[str]: | ||
if len(self.get_ast().body) > 0: | ||
expr = self.get_ast().body[0] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ | |
from __future__ import annotations | ||
|
||
import typing | ||
from typing import Union | ||
from typing import Optional, List, Union | ||
|
||
from rope.base import ( | ||
codeanalyze, | ||
|
@@ -310,8 +310,8 @@ def _is_variable(self, pyname): | |
|
||
def get_changes( | ||
self, | ||
dest: Union[None, str, resources.Resource], | ||
resources=None, | ||
dest: Optional[Union[str, resources.Resource]], | ||
resources: Optional[List[resources.File]] = None, | ||
task_handle=taskhandle.DEFAULT_TASK_HANDLE, | ||
): | ||
"""Return the changes needed for this refactoring | ||
|
@@ -324,15 +324,21 @@ def get_changes( | |
will be applied to all python files. | ||
|
||
""" | ||
# To do (in another PR): Create a new var: dest_resource: resource.Reource | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't really recommend creating a new argument here. get_changes() is a public API, pretty much all integrations that supports refactoring would have directly called it. I would be very cautious on changing the API here as it's likely going to break some integrations. What I'd recommend here is to annotate IMO, we don't need to maintain backwards compatibility with type annotation. If static checks doesn't pass in older Python version, as long as the code is unaffected at runtime, that's perfectly fine to me. Alternatively, we could add development dependency on typing-extensions. which has a backport of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment wasn't clear. I wasn't suggesting changing the api, I was suggesting adding a new var (not argument). This change would be an example of what I called (in another reply) "splitting" a var. mypy will complain about vars that hold values of differing types. Imo, it's no big deal whether the existing code ever changes to please mypy. |
||
# Doing so should remove the type:ignore below. | ||
if isinstance(dest, str): | ||
dest = self.project.find_module(dest) | ||
if resources is None: | ||
resources = self.project.get_python_files() | ||
# The previous guards protect against this mypy complaint: | ||
# "Resource" has no attribute "has_child" | ||
if dest is None or not dest.exists(): | ||
raise exceptions.RefactoringError("Move destination does not exist.") | ||
if dest.is_folder() and dest.has_child("__init__.py"): | ||
dest = dest.get_child("__init__.py") | ||
if dest.is_folder(): | ||
if dest.is_folder() and dest.has_child("__init__.py"): # type:ignore | ||
dest = dest.get_child("__init__.py") # type:ignore | ||
# The previous guards protect against this mypy complaint: | ||
# Item "None" of "Union[str, Resource, None]" has no attribute "is_folder" | ||
if dest.is_folder(): # type:ignore | ||
raise exceptions.RefactoringError( | ||
"Move destination for non-modules should not be folders." | ||
) | ||
|
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 a fan of sprinkling so many
type: ignore
everywhere. Is there any way to tell mypy to just ignore untyped modules by default.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.
@lieryan There are several ways of telling mypy to ignore untyped modules:
type: ignore
as the very first line ofrope.base.prefs
to suppress all checks there..mypy
config files supposedly can suppress checks to specific modules.However, this capability seems buggy. It seems to suppress initial checks,
but inferences in other files seem to "bleed into" the supposedly suppressed modules.
One alternative to
type: ignore
is annotating something asAny
. However, this just substitutes one suppression pattern for another.Let's look at the
type: ignore
statements in this PR:type: ignore
on line 8 ofrope.base.ast
is a special case. I don't know any way to remove it.fscommands
,doa
,runmod
,numpydocstrings
, andproject
suppresswarnings about third-party imports that might fail. These suppressions seem harmless and natural.
oi.type_hinting_evaluate
: suppress (valid) mypy complaints about the@multi
decorator.I don't understand why
@multi
is needed, so I can't suggest any alternative.My guess:
type: ignore
is likely the only way.type: ignore
incontrib.autoimport.sqlite
,but I didn't try very hard—this file doesn't interest me at present.
type: ignore
suppressions inrefactor.move
can only be removed by "splitting" a name which is assigned values with different types.This coding style becomes natural when using mypy. In my experience it always clarifies the code.
But this PR avoids substantive changes to the code.
Summary
Most
type: ignore
suppressions seem harmless. The others seem unavoidable.I'm not bothered by the
type: ignore
comments. There aren't that many, and I've carefully documented all the unusual suppressions.