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

[red-knot] Implicit instance attributes #15811

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sharkdp
Copy link
Contributor

@sharkdp sharkdp commented Jan 29, 2025

Summary

Add support for implicitly-defined instance attributes, i.e. support type inference for cases like this:

class C:
    def __init__(self) -> None:
        self.x: int = 1

reveal_type(C().x)  # int

To do:

  • Support explicitly declared attribute assignments (self.x: str = "a")
  • Support declarations (self.x: str)
  • Support non-annotated assignments (self.x = "a")
  • Build unions of inferred types, if there are multiple assignments
  • Write tests for nested classes
  • Write tests for functions nested inside __init__
  • Write tests for attributes whose type should be a union from different assignments (in multiple functions).
  • Write tests for something like self.x, self.y = …
  • Write tests for conditional function definitions

Things intentionally left out of this PR:

  • Type inference for self
  • Support attributes which are implicitly "declared" via their parameter types (self.x = param)
  • Tuple-unpacking attribute assignments: self.x, self.y = …
  • Diagnostic for conflicting declared types

Test Plan

Updated and new Markdown tests

@sharkdp sharkdp added the red-knot Multi-file analysis & type inference label Jan 29, 2025

# TODO: Same here. This should be `Unknown | Literal[1, "a"]`
reveal_type(c_instance.inferred_from_other_attribute) # revealed: @Todo(implicit instance attribute)
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this doesn't work because we don't know what the type of self is.


# TODO: Should be `Unknown | Literal[1, "a"]`
reveal_type(c_instance.inferred_from_other_attribute) # revealed: @Todo(implicit instance attribute)
reveal_type(c_instance.inferred_from_other_attribute) # revealed: Unknown
Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above: this does not work because we don't know what the type of self is.

@@ -266,7 +259,7 @@ reveal_type(C.pure_class_variable) # revealed: Unknown

c_instance = C()
# TODO: should be `Literal["overwritten on class"]`
reveal_type(c_instance.pure_class_variable) # revealed: @Todo(implicit instance attribute)
reveal_type(c_instance.pure_class_variable) # revealed: Unknown | Literal["value set in class method"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably not something that we're going to address here. This would require narrowing on attribute expressions. Unknown | Literal["value set in class method"] is not wrong.

@sharkdp sharkdp force-pushed the david/implicit-instance-attributes branch from b216fc3 to 9f5ffe1 Compare January 30, 2025 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant