Skip to content

Commit cbaf304

Browse files
authored
Merge branch 'master' into update-docs
2 parents 739dcf0 + 48942a9 commit cbaf304

File tree

7 files changed

+107
-14
lines changed

7 files changed

+107
-14
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ python:
55
- "3.3"
66
- "3.4"
77
- "3.5"
8+
- "3.6"
89
- "pypy"
910

1011
sudo: false
1112

12-
cache:
13-
directories:
14-
- $HOME/.cache/pip
13+
cache: pip
1514

1615
env:
1716
- USE_OPTIONAL=true

html5lib/filters/alphabeticalattributes.py

Lines changed: 12 additions & 1 deletion
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
Lines changed: 81 additions & 0 deletions
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

Lines changed: 6 additions & 8 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

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

44
[testenv]
55
deps =
@@ -14,7 +14,7 @@ deps =
1414
doc: Sphinx
1515

1616
commands =
17-
{envbindir}/py.test
17+
{envbindir}/py.test {posargs}
1818
{toxinidir}/flake8-run.sh
1919

2020
[testenv:doc]

0 commit comments

Comments
 (0)