diff options
author | xmo-odoo <xmo@odoo.com> | 2022-05-17 10:22:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-17 10:22:31 +0200 |
commit | a90d0ee11685fef61e61c2de01a417a0e26eba50 (patch) | |
tree | 3d4e2d56c9ec1b8d636d8d9054d4e1c3ef249d73 | |
parent | 58c10b06e5239a68a1a0c7cb311402581b4e20d1 (diff) | |
download | python-lxml-a90d0ee11685fef61e61c2de01a417a0e26eba50.tar.gz |
Fix inheritance order of mixin classes in lxml.html (GH-340)
As the old FIXME comment from
https://github.com/lxml/lxml/commit/8132c755adad4a75ba855d985dd257493bccc7fd
notes, the mixin should come first for the inheritance to be correct (the left-most class is the
first in the MRO, at least if no diamond inheritance is involved).
Also fix the odd `super` call in `HtmlMixin`, likely stemming from the incorrect MRO.
Fixes the inheritance order of all `HTML*` base classes though it
probably doesn't matter for other than `HtmlElement`.
-rw-r--r-- | src/lxml/html/__init__.py | 14 | ||||
-rw-r--r-- | src/lxml/html/tests/test_basic.py | 44 | ||||
-rw-r--r-- | tox.ini | 1 |
3 files changed, 49 insertions, 10 deletions
diff --git a/src/lxml/html/__init__.py b/src/lxml/html/__init__.py index 2139c75a..ef06a40b 100644 --- a/src/lxml/html/__init__.py +++ b/src/lxml/html/__init__.py @@ -245,7 +245,7 @@ class HtmlMixin(object): creates a 'boolean' attribute without value, e.g. "<form novalidate></form>" for ``form.set('novalidate')``. """ - super(HtmlElement, self).set(key, value) + super(HtmlMixin, self).set(key, value) @property def classes(self): @@ -685,21 +685,19 @@ iterlinks = _MethodFunc('iterlinks', copy=False) rewrite_links = _MethodFunc('rewrite_links', copy=True) -class HtmlComment(etree.CommentBase, HtmlMixin): +class HtmlComment(HtmlMixin, etree.CommentBase): pass -class HtmlElement(etree.ElementBase, HtmlMixin): - # Override etree.ElementBase.cssselect() and set(), despite the MRO (FIXME: change base order?) - cssselect = HtmlMixin.cssselect - set = HtmlMixin.set +class HtmlElement(HtmlMixin, etree.ElementBase): + pass -class HtmlProcessingInstruction(etree.PIBase, HtmlMixin): +class HtmlProcessingInstruction(HtmlMixin, etree.PIBase): pass -class HtmlEntity(etree.EntityBase, HtmlMixin): +class HtmlEntity(HtmlMixin, etree.EntityBase): pass diff --git a/src/lxml/html/tests/test_basic.py b/src/lxml/html/tests/test_basic.py index 6e35c274..464d4747 100644 --- a/src/lxml/html/tests/test_basic.py +++ b/src/lxml/html/tests/test_basic.py @@ -1,11 +1,51 @@ +import sys import unittest from lxml.tests.common_imports import make_doctest, doctest -import lxml.html +from lxml import html + +class TestBasicFeatures(unittest.TestCase): + def test_various_mixins(self): + base_url = "http://example.org" + doc = html.fromstring(""" + <root> + <!-- comment --> + <?pi contents ?> + &entity; + <el/> + </root> + """, base_url=base_url) + self.assertEqual(doc.getroottree().docinfo.URL, base_url) + self.assertEqual(len(doc), 3) + self.assertIsInstance(doc[0], html.HtmlComment) + self.assertIsInstance(doc[1], html.HtmlProcessingInstruction) + self.assertIsInstance(doc[2], html.HtmlElement) + for child in doc: + # base_url makes sense on all nodes (kinda) whereas `classes` or + # `get_rel_links` not really + self.assertEqual(child.base_url, base_url) + + def test_set_empty_attribute(self): + e = html.Element('e') + e.set('a') + e.set('b', None) + e.set('c', '') + self.assertEqual( + html.tostring(e), + b'<e a b c=""></e>', + "Attributes set to `None` should yield empty attributes" + ) + self.assertEqual(e.get('a'), '', "getting the empty attribute results in an empty string") + self.assertEqual(e.attrib, { + 'a': '', + 'b': '', + 'c': '', + }) def test_suite(): suite = unittest.TestSuite() suite.addTests([make_doctest('test_basic.txt')]) - suite.addTests([doctest.DocTestSuite(lxml.html)]) + suite.addTests([doctest.DocTestSuite(html)]) + suite.addTest(unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])) return suite if __name__ == '__main__': @@ -7,6 +7,7 @@ envlist = py27, py35, py36, py37, py38, py39, py310 [testenv] +allowlist_externals = make setenv = CFLAGS = -g -O0 commands = |