Skip to content
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

Add some inference annotations #1887

Merged
merged 18 commits into from
Dec 15, 2022

Conversation

nickdrozd
Copy link
Contributor

Type of Changes

Type
🔨 Refactoring

@coveralls
Copy link

coveralls commented Nov 27, 2022

Pull Request Test Coverage Report for Build 3700007049

  • 27 of 27 (100.0%) changed or added relevant lines in 5 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 92.285%

Totals Coverage Status
Change from base Build 3678470532: 0.0%
Covered Lines: 9905
Relevant Lines: 10733

💛 - Coveralls

Copy link
Collaborator

@DanielNoord DanielNoord left a comment

Choose a reason for hiding this comment

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

Initial pass!

@@ -89,15 +89,17 @@ def __get__(self, inst, objtype=None):
return val


def path_wrapper(func):
def path_wrapper(func: Callable) -> Callable:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should probably be a Typevar or even a ParamSpec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you explain more? I haven't used those.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the PEP introducing ParamSpec is quite clear:
https://peps.python.org/pep-0612/

It explains its uses and those can be applied here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That sounds out of scope for this PR. I can undo this change if you want.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, let's revert then

astroid/helpers.py Outdated Show resolved Hide resolved
def _infer_sequence_helper(node, context: InferenceContext | None = None):
def _infer_sequence_helper(
node: _BaseContainerT, context: InferenceContext | None = None
) -> list[nodes.NodeNG]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is node.elts correctly typed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BaseContainer has self.elts: list[NodeNG] = []

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm that sounds weird. I need to investigate this but I'm fairly certain that it could be Instance as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I checked and this is likely incorrect. In the construction of Lists in DictModel.attr_values we use the keys of a Dict.items. Those are SuccessfulInferenceResults.
So elts is indeed incorrectly typed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should this change be undone?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't you change it to SuccessfulInferenceResult?

astroid/inference.py Show resolved Hide resolved
astroid/inference.py Outdated Show resolved Hide resolved
def _populate_context_lookup(call, context):
def _populate_context_lookup(
call: nodes.Call, context: InferenceContext | None
) -> dict[nodes.NodeNG, InferenceContext]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels like it should be InferenceResult or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Which part?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The nodes.NodeNG

astroid/interpreter/dunder_lookup.py Show resolved Hide resolved
astroid/nodes/_base_nodes.py Outdated Show resolved Hide resolved
astroid/protocols.py Outdated Show resolved Hide resolved
@Pierre-Sassoulas Pierre-Sassoulas added the Maintenance Discussion or action around maintaining astroid or the dev workflow label Nov 28, 2022
@DanielNoord DanielNoord self-requested a review November 29, 2022 17:03
Copy link
Collaborator

@DanielNoord DanielNoord left a comment

Choose a reason for hiding this comment

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

Did another pass! Thanks for taking the time to do this, I know how difficult it is to get these right and PRs like this are really helpful to at least get the discussion and project going 😄

def wrapped(node, context: InferenceContext | None = None, _func=func, **kwargs):
def wrapped(
node, context: InferenceContext | None = None, _func=func, **kwargs
) -> Iterator:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's use Generator, since this function has return statements.

@@ -219,7 +219,7 @@ def is_supertype(type1, type2) -> bool:
return _type_check(type1, type2)


def class_instance_as_index(node):
def class_instance_as_index(node) -> nodes.Const | None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def class_instance_as_index(node) -> nodes.Const | None:
def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:

I did a quick check and this should work 😄

def _infer_sequence_helper(node, context: InferenceContext | None = None):
def _infer_sequence_helper(
node: _BaseContainerT, context: InferenceContext | None = None
) -> list[nodes.NodeNG]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I checked and this is likely incorrect. In the construction of Lists in DictModel.attr_values we use the keys of a Dict.items. Those are SuccessfulInferenceResults.
So elts is indeed incorrectly typed.

@@ -216,7 +218,7 @@ def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
while current.parent and not isinstance(current.parent, nodes.FunctionDef):
current = current.parent
if current and current.parent:
return current.parent # type: ignore[return-value]
return current.parent
Copy link
Collaborator

Choose a reason for hiding this comment

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

This causes an additional message.

@@ -743,7 +745,9 @@ def _bin_op(
)


def _get_binop_contexts(context, left, right):
def _get_binop_contexts(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's do this in my BinOp PRs. I'll prepare those somewhere this week.

@@ -759,7 +763,7 @@ def _get_binop_contexts(context, left, right):
yield new_context


def _same_type(type1, type2):
def _same_type(type1, type2) -> bool:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is interesting as adding annotations to type1 will cause a type error... Hmm, we should probably investigate this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function is used twice. In both cases, the inputs can be InferenceResult | None, but this function assumes that it will be InferenceResult.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Proxy is involved too. Messy!

Copy link
Collaborator

Choose a reason for hiding this comment

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

😅

@@ -349,7 +349,9 @@ def assend_assigned_stmts(
nodes.AssignAttr.assigned_stmts = assend_assigned_stmts


def _arguments_infer_argname(self, name, context):
def _arguments_infer_argname(
self, name: str, context: InferenceContext
Copy link
Collaborator

Choose a reason for hiding this comment

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

name can be None, see code down below.

pyproject.toml Outdated Show resolved Hide resolved
@@ -89,15 +89,17 @@ def __get__(self, inst, objtype=None):
return val


def path_wrapper(func):
def path_wrapper(func: Callable) -> Callable:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, let's revert then

def _infer_sequence_helper(node, context: InferenceContext | None = None):
def _infer_sequence_helper(
node: _BaseContainerT, context: InferenceContext | None = None
) -> list[nodes.NodeNG]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't you change it to SuccessfulInferenceResult?

@@ -759,7 +763,7 @@ def _get_binop_contexts(context, left, right):
yield new_context


def _same_type(type1, type2):
def _same_type(type1, type2) -> bool:
Copy link
Collaborator

Choose a reason for hiding this comment

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

😅

def _populate_context_lookup(call, context):
def _populate_context_lookup(
call: nodes.Call, context: InferenceContext | None
) -> dict[InferenceResult, InferenceContext]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have you actually checked that this is InferenceResult and not SuccesfulInferenceResult?

Copy link
Member

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Choose a reason for hiding this comment

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

Looks pretty good, there are a lot of fixes. I would be in favor of merging. We definitely need to activate mypy to be able to tell if something regressed without having to check locally by comparing the output before and after (I didn't do that so the approve is based on trust and the fact that actually verifying would take a lot of time.)

Copy link
Collaborator

@DanielNoord DanielNoord left a comment

Choose a reason for hiding this comment

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

I did verify this last time and all comments have been resolved! Thanks!

@DanielNoord DanielNoord merged commit 583a279 into pylint-dev:main Dec 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Maintenance Discussion or action around maintaining astroid or the dev workflow
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants