-
Notifications
You must be signed in to change notification settings - Fork 8
Replace factory functions with classes. #81
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
base: main
Are you sure you want to change the base?
Conversation
Element() and ElementTree() were factory functions until now. They are now defined as classes. With this change, we can annotate code with the public Element class, instead of using the private _Element class. For example: ``` def print_tree(tree: e.ElementTree[e.Element]) -> None: ... ``` There is a related PR for lxml: lxml/lxml#405 In the lxml PR, _Element is a virtual subclass of Element. This means that when we create an Element it actually creates an _Element class, but the created object is also an instance of Element: ``` el = Element() reveal_type(el) # _Element isinstance(element, Element) # True isinstance(element, _Element) # True ``` I don't know if it is possible to have the equivalent of a virtual subclass in a type stub. Therefore, I just made Element a type alias of _Element. The lxms stubs should work with all existing versions of lxml. But I am not sure it make sense to merge this PR until the related PR for lxml is merged.
Yeah, it makes more sense to wait for I have taken a glimpse of your Such change needs some time for testing; luckily there's spare time until |
I was thinking of breaking this change over several PRs:
I can easily create a PR for step 2 or add it to this PR. The only down side is that it changes almost 100 lines of code, so it makes it harder to see what are interesting changes in this PR. Step 3 subtly breaks the runtime tests unless one uses a patched version of lxml. I think that the problem is in the tests, but this is not the important point. What is important is that step 3 has the potential of breaking existing code that does run-time type checking. It is probably very rare, but we shouldn't make a release of types-lxml with step 3 before lxml 6.0.0 (with the Element change) is released. Step 4 would essentially break any existing code that uses types-lxml. It might be possible to implement it in a few years, but maybe an _Element type alias should be kept forever. |
I'm aware that your PR in |
Things don't seem right for me: >>> from lxml import etree
>>> etree.LXML_VERSION
(6, 0, 0, -200)
>>> root = etree.Element('foo')
>>> type(root).__mro__
(<class 'lxml.etree._Element'>, <class 'object'>) Are you expecting |
Sort of. See lxml/lxml#405 for the full details.
_Element is not an actual subclass of Element, it is just registered as a virtual subclass of Element.
This way
And at the same time _Element stays a cython class, which is an internal implementation detail and does not depend directly on Element. For the types stubs, it is a bit simpler. Element is just a type alias to _Element. Technically, it would be possible to get rid of _Element completely, but this would break backward compatibility. |
PR lxml/lxml#461 - Enable subscripting generic types in annotations - was just merged into lxml. This means that with lxml-6.0 one could write:
and Python would accept it even if you do not use: It would be really nice if the lxml stubs would also allow this syntax for the lxml-6.0 release. |
Sure, I noticed the merge yesterday. It was a surprise, given that similar effort has been rejected before. |
I was able to reproduce the error locally by running the latest version of pyright. I am not sure what version of pyright is used by github actions. The last commit removes the duplicate definition of |
Yeah, the
So I bet you don't want to only apply a single fix locally, only to conflict with a larger scale warning fix I'm going to apply to main. Maybe you can revert your last 2 commits and merge The duplicate definitions are another story. I added them for very specific reason. Take >>> from lxml.html import fromstring
>>> root = fromstring("<html><body><br/></body></html>")
>>> elem = root.makeelement("form")
>>> type(elem)
<class 'lxml.html.FormElement'>
>>> elem = root.makeelement("label")
>>> type(elem)
<class 'lxml.html.LabelElement'> If root node is |
Your example with Static checks work correctly without the duplicate definition thanks to the
In any case, I reverted my last two commits like you asked. |
Thanks, I have just committed a fix, and you can rebase your work on that.
The issue here is, I am taking care of real world usage when designing those incompatible overrides. The usage of
When fixing stub for |
Ah sorry, by "revert" I mean
then force push the branch, so the conflict is completely gone. |
I merged your commits to my branch. Notice that |
No problem, I'll redo the patch and commit directly to your branch. |
lxml 6.0 was released a few weeks ago with the change that Is there anything holding back merging this PR? |
@udifuchs Sorry for late reply.
Yeah, a few reasons actually.
|
There is not much difference between a class and a factory function. Both are considered
AFAIK, this PR is fully compatible with both lxml 5.x and 6.0. Do you have any examples of incompatible code? |
OK, I should have mentioned I was working on 2 PRs together, so it was not apparent to you: for example, I can silent the warnings and see if it works in real world. As long as nobody complains, it's good to go. |
db4728e
to
fffa267
Compare
The merge with main was becoming a mess because there have been too many commits in between. Let me roll back to prestine state and work from there instead. |
element: None = ..., | ||
*, | ||
file: _t._FileReadSource, | ||
parser: CustomTargetParser[_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.
🚫 [mypy] reported by reviewdog 🐶
Name "CustomTargetParser" is not defined [name-defined]
*, | ||
file: _t._FileReadSource, | ||
parser: CustomTargetParser[_T], | ||
) -> _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.
🚫 [mypy] reported by reviewdog 🐶
A function returning TypeVar should receive at least one argument containing the same TypeVar [type-var]
element: None = ..., | ||
*, | ||
file: _t._FileReadSource, | ||
parser: CustomTargetParser[_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.
🚫 [pyright] reported by reviewdog 🐶
Type of parameter "parser" is unknown (reportUnknownParameterType)
element: None = ..., | ||
*, | ||
file: _t._FileReadSource, | ||
parser: CustomTargetParser[_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.
🚫 [pyright] reported by reviewdog 🐶
"CustomTargetParser" is not defined (reportUndefinedVariable)
@udifuchs It turns out rebasing a large change against almost 200 commits turns out to be disastrous, with way too many conflict resolution that I have lost track somewhere in between. Can you rework the patch against current head and submit a new PR? BTW I have added a few patches to this PR, you will need to fix those parts in new PR. |
Element() and ElementTree() were factory functions until now. They are now defined as classes.
With this change, we can annotate code with the public Element class, instead of using the private _Element class.
For example:
There is a related PR for lxml:
lxml/lxml#405
In the lxml PR, _Element is a virtual subclass of Element. This means that when we create an Element it actually creates an _Element class, but the created object is also an instance of Element:
I don't know if it is possible to have the equivalent of a virtual subclass in a type stub. Therefore, I just made Element a type alias of _Element.
The lxms stubs should work with all existing versions of lxml. But I am not sure it make sense to merge this PR until the related PR for lxml is merged.