8000 Preliminary work on a form-filler filter · html5lib/html5lib-python@8f6e2e7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8f6e2e7

Browse files
committed
Preliminary work on a form-filler filter
(for the moment a simple form-filler for HTML4 documents, assuming there is a single form or there aren't conflicting field names) --HG-- extra : convert_revision : svn%3Aacbfec75-9323-0410-a652-858a13e371e0/trunk%40913
1 parent c851900 commit 8f6e2e7

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

src/html5lib/filters/formfiller.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#
2+
# The goal is to finally have a form filler where you pass data for
3+
# each form, using the algorithm for "Seeding a form with initial values"
4+
# See http://www.whatwg.org/specs/web-forms/current-work/#seeding
5+
#
6+
7+
import _base
8+
9+
from html5lib.constants import spaceCharacters
10+
spaceCharacters = u"".join(spaceCharacters)
11+
12+
class SimpleFilter(_base.Filter):
13+
def __init__(self, source, fieldStorage):
14+
_base.Filter.__init__(self, source)
15+
self.fieldStorage = fieldStorage
16+
17+
def __iter__(self):
18+
field_indices = {}
19+
state = None
20+
field_name = None
21+
for token in _base.Filter.__iter__(self):
22+
type = token["type"]
23+
if type in ("StartTag", "EmptyTag"):
24+
name = token["name"].lower()
25+
if name == "input":
26+
field_name = None
27+
field_type = None
28+
input_value_index = -1
29+
input_checked_index = -1
30+
for i,(n,v) in enumerate(token["data"]):
31+
n = n.lower()
32+
if n == "name":
33+
field_name = v.strip(spaceCharacters)
34+
elif n == "type":
35+
field_type = v.strip(spaceCharacters)
36+
elif n == "checked":
37+
input_checked_index = i
38+
elif n == "value":
39+
input_value_index = i
40+
41+
value_list = self.fieldStorage.getlist(field_name)
42+
field_index = field_indices.setdefault(field_name, 0)
43+
if field_index < len(value_list):
44+
value = value_list[field_index]
45+
else:
46+
value = ""
47+
48+
if type in ("checkbox", "radio"):
49+
if value_list:
50+
if token["data"][input_value_index] == value:
51+
if input_checked_index < 0:
52+
token["data"].append((u"checked", u""))
53+
field_indices[field_name] = field_index + 1
54+
elif input_checked_index >= 0:
55+
del token["data"][input_checked_index]
56+
57+
elif type not in ("button", "submit", "reset"):
58+
if input_value_index >= 0:
59+
token["data"][input_value_index] = (u"value", value)
60+
else:
61+
token["data"].append((u"value", value))
62+
field_indices[field_name] = field_index + 1
63+
64+
field_type = None
65+
field_name = None
66+
67+
elif name == "textarea":
68+
field_type = "textarea"
69+
field_name = dict(reversed(token["data"]))["name"]
70+
71+
elif name == "select":
72+
field_type = "select"
73+
attributes = dict(reversed(token["data"]))
74+
field_name = attributes.get("name")
75+
is_select_multiple = "multiple" in attributes
76+
77+
elif state == "in_select" and field_name and name == "option":
78+
option_selected_index = -1
79+
option_value = None
80+
for i,(n,v) in enumerate(token["data"]):
81+
n = n.lower()
82+
if n == "selected":
83+
option_selected_index = i
84+
elif n == "value":
85+
option_value = v.strip(spaceCharacters)
86+
if option_value is None:
87+
raise NotImplementedError("<option>s must have a value= attribute")
88+
else:
89+
value_list = self.fieldStorage.getlist(field_name)
90+
if value_list:
91+
field_index = field_indices.setdefault(field_name, 0)
92+
if field_index < len(value_list):
93+
value = value_list[field_index]
94+
else:
95+
value = ""
96+
if option_value == value:
97+
if option_selected_index < 0:
98+
token["data"].append((u"selected", u""))
99+
field_indices[field_name] = field_index + 1
100+
elif option_selected_index >= 0:
101+
del token["data"][option_selected_index]
102+
103+
elif field_type is not None and field_name and type == "EndTag":
104+
name = token["name"].lower()
105+
if name == "textarea":
106+
value_list = self.fieldStorage.getlist(field_name)
107+
if value_list:
108+
field_index = field_indices.setdefault(field_name, 0)
109+
if field_index < len(value_list):
110+
value = value_list[field_index]
111+
else:
112+
value = ""
113+
yield {"type": "Characters", "data": value}
114+
115+
field_indices[field_name] = field_index + 1
116+
field_name = None
117+
118+
elif field_type == "textarea":
119+
continue # ignore token
120+
121+
yield token

tests/test_formfiller.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import unittest
2+
3+
from html5lib.filters.formfiller import SimpleFilter
4+
5+
class FieldStorage(dict):
6+
def getlist(self, name):
7+
l = self[name]
8+
if isinstance(l, list):
9+
return l
10+
elif isinstance(l, tuple) or hasattr(l, '__iter__'):
11+
return list(l)
12+
return [l]
13+
14+
class TestCase(unittest.TestCase):
15+
def runTest(self, input, formdata, expected):
16+
output = list(SimpleFilter(input, formdata))
17+
errorMsg = "\n".join(["\n\nInput:", str(input),
18+
"\nForm data:", str(formdata),
19+
"\nExpected:", str(expected),
20+
"\nReceived:", str(output)])
21+
self.assertEquals(output, expected, errorMsg)
22+
23+
def testSingleTextInputWithValue(self):
24+
self.runTest(
25+
[{"type": u"EmptyTag", "name": u"input",
26+
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", u"quux")]}],
27+
FieldStorage({"foo": "bar"}),
28+
[{"type": u"EmptyTag", "name": u"input",
29+
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", "bar")]}])
30+
31+
def testSingleTextInputWithoutValue(self):
32+
self.runTest(
33+
[{"type": u"EmptyTag", "name": u"input",
34+
"data": [(u"type", u"text"), (u"name", u"foo")]}],
35+
FieldStorage({"foo": "bar"}),
36+
[{"type": u"EmptyTag", "name": u"input",
37+
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", "bar")]}])
38+
39+
def main():
40+
unittest.main()
41+
42+
if __name__ == "__main__":
43+
main()

0 commit comments

Comments
 (0)
0