-
Notifications
You must be signed in to change notification settings - Fork 86
👌 IMPROVE: Make SyntaxTreeNode type annotations work when subclassing #131
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
Codecov Report
@@ Coverage Diff @@
## master #131 +/- ##
==========================================
- Coverage 96.51% 96.43% -0.08%
==========================================
Files 72 72
Lines 3183 3202 +19
==========================================
+ Hits 3072 3088 +16
- Misses 111 114 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
|
class MyTree(SyntaxTreeNodeBase["MyTree"]):
pass
tree = MyTree["MyTree"]()
EDIT: this comment is outdated |
|
If you're wondering why this has to be so complicated in the first place, haha, it's because many interfaces (method input or outputs, public class attrs) in this class reference the type of |
markdown_it/tree.py
Outdated
| closing: Token | ||
|
|
||
|
|
||
| _T = TypeVar("_T", bound="SyntaxTreeNode") |
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.
why _T? it would be better from a readability perspective to use something like NodeType
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.
It seems like its a convention to name TypeVars as T (or if private, _T). I think this is because its short, and because the same TypeVar could be reused in various contexts having a different meaning in each. Here setting the bound sort of limits any reasonable reusability though.
That being said, I don't oppose renaming at all if you think thats better 😄
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 I think so thanks
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.
changed to _NodeType
markdown_it/tree.py
Outdated
| # Empty list unless a non-empty container, or unnested token that has | ||
| # children (i.e. inline or img) | ||
| self.children: List["SyntaxTreeNode"] = [] | ||
| self.children = [] |
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.
should this not be self._children?
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.
There really is no difference, but I think using the public API is less brittle in case someone ever changes the @property setter
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 all internal variables should be set in __init__. TBH I'm surprised flake8 does not fail this, pylint definitely does
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.
The internal vars are set in init, but via the @Property, flake8 understands this.
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.
Reluctantly made this change 😄
markdown_it/tree.py
Outdated
|
|
||
| # Root node does not have self.parent | ||
| self.parent: Optional["SyntaxTreeNode"] = None | ||
| self.parent = None |
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.
should this not be self._parent
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.
same as self._children 😄
markdown_it/tree.py
Outdated
|
|
||
| @classmethod | ||
| def from_tokens(cls, tokens: Sequence[Token]) -> "SyntaxTreeNode": | ||
| def from_tokens(cls: Type[_T], tokens: Sequence[Token]) -> _T: |
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.
do cls and self really need to be annotated?
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.
Yep, the type checker binds a meaning to _T based on context. Here it gets the context from cls (it knows its type) so it deduces that the return type is the same as that.
E.g. if you call node = MyTreeNode.from_tokens(), the type checker then knows that node is MyTreeNode and if the type is something different (another subclass) the type checker binds the TypeVar to that.
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 cheers
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.
To clarify, if you dont annnotate cls, the type checker doesn't have any context useful to binding the TypeVar so the return type will be Any.
|
@chrisjsewell I added the necessary |
This PR makesSyntaxTreeNode.childrenandSyntaxTreeNode.parentread-only properties, as it seems that's by far the least complicated way of making type annotations work expectedly when extending the class. Those properties shouldn't be rewritten anyways so I guess this is not a bad thingEdit: This PR simply makes
SyntaxTreeNode.childrenandSyntaxTreeNode.parent@propertys so that they can be type annotated in a way where the getter return typeTypeVaris automatically bound to type ofself.