Skip to content

Commit 403e687

Browse files
authored
Merge branch 'master' into py36escseq
2 parents efe982b + 10e54c1 commit 403e687

12 files changed

+183
-137
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ python:
55
- "3.3"
66
- "3.4"
77
- "3.5"
8+
- "3.6"
89
- "pypy"
910

1011
sudo: false

AUTHORS.rst

+1
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ Patches and suggestions
4343
- Marc Abramowitz
4444
- Jon Dufresne
4545
- Ville Skyttä
46+
- Jonathan Vanasco

CHANGES.rst

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Change Log
22
----------
33

4+
unreleased
5+
~~~~~~~~~~~~~~~~~~
6+
7+
* Added `itemscope` as boolean attribute
8+
https://github.com/html5lib/html5lib-python/issues/194
9+
410
0.999999999/1.0b10
511
~~~~~~~~~~~~~~~~~~
612

html5lib/constants.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@
588588
])
589589

590590
booleanAttributes = {
591-
"": frozenset(["irrelevant"]),
591+
"": frozenset(["irrelevant", "itemscope"]),
592592
"style": frozenset(["scoped"]),
593593
"img": frozenset(["ismap"]),
594594
"audio": frozenset(["autoplay", "controls"]),

html5lib/filters/alphabeticalattributes.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,24 @@
88
from ordereddict import OrderedDict
99

1010

11+
def _attr_key(attr):
12+
"""Return an appropriate key for an attribute for sorting
13+
14+
Attributes have a namespace that can be either ``None`` or a string. We
15+
can't compare the two because they're different types, so we convert
16+
``None`` to an empty string first.
17+
18+
"""
19+
return (attr[0][0] or ''), attr[0][1]
20+
21+
1122
class Filter(base.Filter):
1223
def __iter__(self):
1324
for token in base.Filter.__iter__(self):
1425
if token["type"] in ("StartTag", "EmptyTag"):
1526
attrs = OrderedDict()
1627
for name, value in sorted(token["data"].items(),
17-
key=lambda x: x[0]):
28+
key=_attr_key):
1829
attrs[name] = value
1930
token["data"] = attrs
2031
yield token

html5lib/tests/serializer-testdata/options.test

+69
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,29 @@
4646
"quote_attr_values": "always"
4747
}
4848
},
49+
{
50+
"expected": [
51+
"<div itemscope>"
52+
],
53+
"input": [
54+
[
55+
"StartTag",
56+
"http://www.w3.org/1999/xhtml",
57+
"div",
58+
[
59+
{
60+
"namespace": null,
61+
"name": "itemscope",
62+
"value": "itemscope"
63+
}
64+
]
65+
]
66+
],
67+
"description": "quote_attr_values='always' with itemscope",
68+
"options": {
69+
"quote_attr_values": "always"
70+
}
71+
},
4972
{
5073
"expected": [
5174
"<div irrelevant>"
@@ -171,6 +194,29 @@
171194
"use_trailing_solidus": true
172195
}
173196
},
197+
{
198+
"expected": [
199+
"<div itemscope=itemscope>"
200+
],
201+
"input": [
202+
[
203+
"StartTag",
204+
"http://www.w3.org/1999/xhtml",
205+
"div",
206+
[
207+
{
208+
"namespace": null,
209+
"name": "itemscope",
210+
"value": "itemscope"
211+
}
212+
]
213+
]
214+
],
215+
"description": "minimize_boolean_attributes=false",
216+
"options": {
217+
"minimize_boolean_attributes": false
218+
}
219+
},
174220
{
175221
"expected": [
176222
"<div irrelevant=irrelevant>"
@@ -194,6 +240,29 @@
194240
"minimize_boolean_attributes": false
195241
}
196242
},
243+
{
244+
"expected": [
245+
"<div itemscope=\"\">"
246+
],
247+
"input": [
248+
[
249+
"StartTag",
250+
"http://www.w3.org/1999/xhtml",
251+
"div",
252+
[
253+
{
254+
"namespace": null,
255+
"name": "itemscope",
256+
"value": ""
257+
}
258+
]
259+
]
260+
],
261+
"description": "minimize_boolean_attributes=false with empty value",
262+
"options": {
263+
"minimize_boolean_attributes": false
264+
}
265+
},
197266
{
198267
"expected": [
199268
"<div irrelevant=\"\">"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from __future__ import absolute_import, division, unicode_literals
2+
3+
try:
4+
from collections import OrderedDict
5+
except ImportError:
6+
from ordereddict import OrderedDict
7+
8+
import pytest
9+
10+
import html5lib
11+
from html5lib.filters.alphabeticalattributes import Filter
12+
from html5lib.serializer import HTMLSerializer
13+
14+
15+
@pytest.mark.parametrize('msg, attrs, expected_attrs', [
16+
(
17+
'no attrs',
18+
{},
19+
{}
20+
),
21+
(
22+
'one attr',
23+
{(None, 'alt'): 'image'},
24+
OrderedDict([((None, 'alt'), 'image')])
25+
),
26+
(
27+
'multiple attrs',
28+
{
29+
(None, 'src'): 'foo',
30+
(None, 'alt'): 'image',
31+
(None, 'style'): 'border: 1px solid black;'
32+
},
33+
OrderedDict([
34+
((None, 'alt'), 'image'),
35+
((None, 'src'), 'foo'),
36+
((None, 'style'), 'border: 1px solid black;')
37+
])
38+
),
39+
])
40+
def test_alphabetizing(msg, attrs, expected_attrs):
41+
tokens = [{'type': 'StartTag', 'name': 'img', 'data': attrs}]
42+
output_tokens = list(Filter(tokens))
43+
44+
attrs = output_tokens[0]['data']
45+
assert attrs == expected_attrs
46+
47+
48+
def test_with_different_namespaces():
49+
tokens = [{
50+
'type': 'StartTag',
51+
'name': 'pattern',
52+
'data': {
53+
(None, 'id'): 'patt1',
54+
('http://www.w3.org/1999/xlink', 'href'): '#patt2'
55+
}
56+
}]
57+
output_tokens = list(Filter(tokens))
58+
59+
attrs = output_tokens[0]['data']
60+
assert attrs == OrderedDict([
61+
((None, 'id'), 'patt1'),
62+
(('http://www.w3.org/1999/xlink', 'href'), '#patt2')
63+
])
64+
65+
66+
def test_with_serializer():
67+
"""Verify filter works in the context of everything else"""
68+
parser = html5lib.HTMLParser()
69+
dom = parser.parseFragment('<svg><pattern xlink:href="#patt2" id="patt1"></svg>')
70+
walker = html5lib.getTreeWalker('etree')
71+
ser = HTMLSerializer(
72+
alphabetical_attributes=True,
73+
quote_attr_values='always'
74+
)
75+
76+
# FIXME(willkg): The "xlink" namespace gets dropped by the serializer. When
77+
# that gets fixed, we can fix this expected result.
78+
assert (
79+
ser.render(walker(dom)) ==
80+
'<svg><pattern id="patt1" href="#patt2"></pattern></svg>'
81+
)

requirements-install.sh

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
#!/bin/bash -ex
22

3-
pip install pip==6.1.0
3+
if [[ $SIX_VERSION ]]; then
4+
pip install six==$SIX_VERSION
5+
fi
46

5-
pip install -U -r requirements-test.txt
7+
pip install -r requirements-test.txt
68

79
if [[ $USE_OPTIONAL == "true" ]]; then
8-
pip install -U -r requirements-optional.txt
9-
fi
10-
11-
if [[ $SIX_VERSION ]]; then
12-
pip install six==$SIX_VERSION
10+
pip install -r requirements-optional.txt
1311
fi
1412

1513
if [[ $CI == "true" ]]; then
16-
pip install -U codecov
14+
pip install codecov
1715
fi

setup.cfg

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ exclude = .git,__pycache__,.tox,doc
99
[flake8]
1010
ignore = N
1111
max-line-length = 139
12+
13+
[metadata]
14+
license_file = LICENSE

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def default_environment():
7070
'Programming Language :: Python :: 3.3',
7171
'Programming Language :: Python :: 3.4',
7272
'Programming Language :: Python :: 3.5',
73+
'Programming Language :: Python :: 3.6',
7374
'Topic :: Software Development :: Libraries :: Python Modules',
7475
'Topic :: Text Processing :: Markup :: HTML'
7576
]

tox.ini

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = {py26,py27,py33,py34,py35,pypy}-{base,optional}
2+
envlist = {py26,py27,py33,py34,py35,py36,pypy}-{base,optional}
33

44
[testenv]
55
deps =
@@ -13,5 +13,5 @@ deps =
1313
optional: -r{toxinidir}/requirements-optional.txt
1414

1515
commands =
16-
{envbindir}/py.test
16+
{envbindir}/py.test {posargs}
1717
{toxinidir}/flake8-run.sh

0 commit comments

Comments
 (0)