Open alonme opened 1 year ago
We have #4813 to use typing when the inference fail, but there's no plan to use typing before inference. (Imo, if we did that we might ad well create a new program from scratch as inference is at the very core of what pylint do). Also I don't understand the use case, in your example number IS a string, treating it like an int will definitely cause issue, right ?
Its a complex case, so i simplified it.
What i would like to do is to tell pylint to specifically use a type instead of infering.
maybe register_transform
with inference_tip
can do the trick?
in the actual case, i am always assigning ...
and then injecting the actual value later into the variable
Something like this i guess?
def infer_my_custom_call(call_node, context=None):
# Do some transformation here
return iter((call_node.annotation,))
astroid.MANAGER.register_transform(
astroid.nodes.AnnAssign,
inference_tip(infer_my_custom_call),
predicate=lambda x: x.value == ...,
)
I would suggest maing this a pylint
extension for now.
@DanielNoord not sure i understand. Are you saying this is achievable using an extension?
Any tips regarding how? I had no luck with the direction that i posted in the last comment
The following seems to work for the examples i currently have, but its kind of ugly
def pylint_cast(t):
if False: # pylint: disable=using-constant-test
return t()
return ...
number: int = pylint_cast(int)
I don't really have an idea about whether this is feasible, I only know that I don't think we should spend time of maintainers (which is already sparse) to explore this. We simply have more pressing issues for us to focus on this future goal.
@alonme there's a bunch of examples about inference tweaking in astroid's "brains" (inference tip) see https://github.com/PyCQA/astroid/tree/main/astroid/brain. You can pretty much do whatever you want for a specific lib this way. Your suggestion to permit to do it on a case by case basis on the user side maybe with pylint: inference:int
or something similar is interesting imo, but also a LOT of design work and then of work. And as Daniel said we have a lot on our plate already.
I created a plugin - which isn't perfect - but works for me for now.
couldn't get it too work with inference_tip
so i used a regular transform
Of course i still believe this should become a feature
from typing import TYPE_CHECKING, Optional
import astroid
from astroid.builder import extract_node
from astroid.nodes.node_classes import AnnAssign, Const, Name, NodeNG, Subscript
if TYPE_CHECKING:
from pylint.lint import PyLinter
def register(linter: "PyLinter") -> None:
"""This required method auto registers the checker during initialization.
:param linter: The linter to register the checker to.
"""
pass
def _is_assigning_ellipsis(node: AnnAssign):
if isinstance(node.value, Const) and node.value.value is ...:
return True
def _handle_subscript(node: Subscript):
if node.value.name == "Optional":
new_node_code = f"{_create_new_code(node.slice)} or None"
elif node.value.name == "Union":
types = [_create_new_code(s) for s in node.slice.elts]
new_node_code = " or ".join(types)
elif node.value.name == "List":
inner_type = _create_new_code(node.slice)
new_node_code = f"list({inner_type})"
elif node.value.name == "Tuple":
types = [_create_new_code(s) for s in node.slice.elts]
new_node_code = f"tuple({', '.join(types)})"
elif node.value.name == "Set":
inner_type = _create_new_code(node.slice)
new_node_code = f"Set({inner_type})"
else:
raise ValueError(f"Unhandled Subscript: {node.value.name}")
return new_node_code
def _create_new_code(call_node: Optional[NodeNG]) -> str:
if isinstance(call_node, Name):
new_node_code = f"{call_node.name}()"
elif isinstance(call_node, Subscript):
new_node_code = _handle_subscript(call_node)
else:
raise ValueError(f"Unhandled node type: {call_node}")
return new_node_code
def _transform_to_annotation_object(call_node: AnnAssign, context=None):
"""
Transform an AnnAssign node to have a value based on the type annotation only
"""
new_node_code = _create_new_code(call_node.annotation)
new_node = extract_node(new_node_code)
# Change the assignment to look as if its value is of the annotation class
call_node.value = new_node
astroid.MANAGER.register_transform(
AnnAssign,
_transform_to_annotation_object,
predicate=_is_assigning_ellipsis,
)
Related (use typing when the inference fail): https://github.com/pylint-dev/pylint/issues/4813. It's a consensual first step imo.
Question
I have a use case in which i want to force pylint to use the type hint of a variable, instead of the inferred value.
For example i have the following code
pylint will treat
number
as astr
, but i want it to treat it as anint
.Is this possible in any way?
Documentation for future user
If there is a feature like that, i couldn't find it easily in the docs
Additional context
No response