-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Add common plugin API available for all hooks #6044
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
Conversation
Btw, @mkurnikov you may be interested in this. The pattern with global state (like |
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.
Is the reason this is required that the generated base class is not stored in the symbol table? Otherwise, you could look up the SymbolNode
for the base class using the full name, right?
Have you considered the alternative of making sure that all generated classes are included in the symbol table? I wonder if we there could be other places where we'd want to look up the TypeInfo
using fullname. This wouldn't help with those.
@@ -5586,3 +5586,75 @@ python_version=3.6 | |||
[out] | |||
[out2] | |||
tmp/a.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str") | |||
|
|||
[case testBaseClassPluginHookWorksIncremental] |
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'd move this to check-custom-plugin.test
and move the plugin code to a separate file.
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.
OK, makes sense.
No, the problem is that some user defined classes are special for some plugins, so the plugin can't know the set of names on which it should trigger. Previously, I tried work around this by storing all class names found to be "magic" in a plugin global state, but this doesn't work in incremental mode |
Hm, indeed, |
Sorry, I don't understand this. What's the problem with using the |
Initially we pass only full name to various |
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.
Lg apart from Jukka’s suggestion.
@JukkaL I updated the PR as we discussed. Also as you proposed, I will add the docs in a separate PR. @msullivan Could you please check that I didn't break anything mypyc-related? |
Also @msullivan now that default plugin implementation moved out of |
(Updated the title and PR description.) |
Seems good. |
IIRC, the main rationale for these hacks was performance. It allows the default modules to be called more efficiently than if the module interface was interpreted. |
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 think that this adds additional tech debt which we should be able to avoid pretty easily. See my comment below.
mypy/checker.py
Outdated
@@ -3568,6 +3568,20 @@ def lookup_qualified(self, name: str) -> SymbolTableNode: | |||
msg = "Failed qualified lookup: '{}' (fullname = '{}')." | |||
raise KeyError(msg.format(last, name)) | |||
|
|||
def lookup_fully_qualified_or_none(self, fullname: str) -> Optional[SymbolTableNode]: |
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'd rather not add yet another lookup function, even if we are planning to refactor this soon. Can you move this to a new module, such as mypy.lookup
? This can be a top-level function that accepts a modules dictionary and fullname. You can then pass the modules dictionary to the plugin, and the plugin can call this function (also the semantic analyzer can call the new function).
Passing the modules dictionary to plugins would have a few additional benefits: it only needs to be done once per build, and the plugins would be given less access to semantic analyzer and type checker internals.
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, this makes sense. I incidentally pushed before fixing some things, I will one more commit soon.
@JukkaL OK, I updated the PR as you suggested. I took the implementation of I will update the docs PR later today. |
Can this somehow be automated in mypyc? Updating both |
@@ -47,6 +50,21 @@ def analyze_callable_args(self, arglist: TypeList) -> Optional[Tuple[List[Type], | |||
('api', TypeAnalyzerPluginInterface)]) | |||
|
|||
|
|||
@trait | |||
class CommonPluginApi: |
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.
Is this class needed any more?
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 like to have it for documentation purpose, to separate thing that should be overridden (like get_xxx_hook()
methods) from things that can be used (like options
and lookup_fully_qualified()
). We can however remove it if you don't like it.
# TODO: gradually move existing lookup functions to this module. | ||
|
||
|
||
def lookup_fully_qualified(name: str, modules: Dict[str, MypyFile], |
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.
Can you verify that this new lookup method doesn't cause a significant slowdown using compiled mypy? The original method was simpler and if there is a performance impact, it may make sense to keep the original method as an alternative/optimization (but carefully document it as such).
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 decided to keep the old look-up function in semanal.py
anyway. It doesn't really belong to this PR. We can make a separate PR that cleans up all lookup related things.
@JukkaL @gvanrossum are you happy with the PR now? |
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.
LG now.
AppVeyor build succeeded but somehow still shown as running here, I am going to merge now. |
There is a problem with
get_base_class_hook()
: only the full name of the base class is passed to it. This causes problems when this hook needs to interact with other hooks. In normal mode this can be solved by using a global plugin state, but this doesn't work in incremental mode, since this plugin state is not stored in cache. There is however already a field that is stored in cache between incremental runs and is free to use by plugins:TypeInfo.metadata
.The problem however is that plugin hooks get only full names and can't access the corresponding
TypeInfo
. This is a common enough problem that we should add an API to make the nodes referred by full names accessible to the plugin hooks. This is done by adding a common lookup method to thePlugin
class.