Description
HelpOnMacros/Include states that the third parameter is an optional heading level. Several examples are given where this parameter is not given, and those examples work. However, if you include the second parameter, the heading title, and the fourth or later parameter, then the heading level is not optional.
Note: This bug was MoinMoinBugs/IncludeHeadingMishandlesQuotes, but that page title doesn't fit the actual problem.
Example
'''Include without headding and To ''succeed'' ''' [[Include(/IncludeTest, , ,to="^----")]] '''Include with Heading and To ''Fails'' ''' [[Include(/IncludeTest, 'Heading', ,to="^----")]]
Include without headding and To succeed
Text to be included
More text
And then some
Even more...
Include with Heading and To Fails
Heading
Text to be included
More text
And then some
Even more...
Details
This Wiki (but since ../IncludeNotCacheAware, it is overlooked)
Workaround
If you use heading and another parameter, always include the level.
Discussion
The offending line is line 22 in macro/Include.py:
_arg_level = r',\s*(?P<level>\d+)'
the fix is to allow the level group to match nothing, by adding a '?' to the expression:
_arg_level = r',\s*(?P<level>\d+)?'
I've written a test procedure, Include_Test.py, which tests the arguments on HelpOnMacros/Include as well as the new ones I introduced above. It tests it under the old code first, to show the test fails, and then with the new code, to show all tests pass.
1 """
2 Matching tests for Include macro
3
4 This module tests the matching ability of the Include regular expression
5 The following URL contains what should be valid examples:
6
7 http://purl.net/wiki/moinmaster/HelpOnMacros/Include
8
9 """
10
11 """
12 The test set is a list of dictionarys, where each dictionary contains:
13 The test string ('test', 'test-string'),
14 The name string ('name', 'name-string'),
15 and these optional values:
16 heading, hquote, htext, level, fquote, from, tquote, to, sort, items,
17 skipitems, titlesonly
18 The items correspond to the named fields in the regular expression
19 """
20
21 test_sets = [
22 dict([('test','FooBar'), ('name','FooBar')]),
23 dict([('test','FooBar, '), ('name','FooBar'), ('heading',',')]),
24 dict([('test','FooBar, , 2'), ('name','FooBar'), ('heading', ','),
25 ('level','2')]),
26 dict([('test',"FooBar, 'All about Foo Bar', 2"), ('name','FooBar'),
27 ('heading',','), ('hquote',"'"), ('htext',"All about Foo Bar"),
28 ('level','2')]),
29 dict([('test','FooBar, , from="^----$"'), ('name','FooBar'),
30 ('heading',','), ('fquote', '"'), ('from','^----$')]),
31 dict([('test','FooBar, , to="^----$"'), ('name','FooBar'),
32 ('heading',','), ('tquote','"'), ('to','^----$')]),
33 dict([('test','^FooBar/.*, , sort=descending'), ('name','^FooBar/.*'),
34 ('heading',','), ('sort','descending')]),
35 dict([('test','^FooBar/.*, , items=3'), ('name','^FooBar/.*'),
36 ('heading',','), ('items','3')]),
37 dict([('test','^BlogBase/.*,, to="^----$", sort=descending, items=7'),
38 ('name','^BlogBase/.*'), ('heading',','), ('tquote','"'),
39 ('to','^----$'), ('sort','descending'), ('items','7')]),
40 dict([('test','^BlogBase/.*,, to="^----$", sort=descending, items=7, skipitems=7,titlesonly'),
41 ('name','^BlogBase/.*'), ('heading',','), ('tquote','"'),
42 ('to','^----$'), ('sort','descending'), ('items','7'),
43 ('skipitems','7'),('titlesonly','titlesonly')]),
44 dict([('test','^FirstnameLastname/20..-..-..,,to="^----",sort=descending,items=3'),
45 ('name','^FirstnameLastname/20..-..-..'), ('heading',','),
46 ('tquote','"'),('to','^----'),('sort','descending'),('items','3')]),
47 dict([('test','^FirstnameLastname/20..-..-..,,to="^----",sort=descending,items=4,skipitems=3,titlesonly'),
48 ('name','^FirstnameLastname/20..-..-..'), ('heading',','),
49 ('tquote','"'),('to','^----'),('sort','descending'),('items','4'),
50 ('skipitems','3'),('titlesonly','titlesonly')]),
51 dict([('test', 'TitleTest, "Heading for Title Test 1",1,to="^----$"'), ('name', 'TitleTest'),
52 ('heading',','), ('hquote', '"'), ('htext', 'Heading for Title Test 1'),
53 ('level','1'),('to', '^----$')]),
54 dict([('test', 'TitleTest, "Heading for Title Test 2", ,to="^----$"'), ('name', 'TitleTest'),
55 ('heading',','), ('hquote', '"'), ('htext', 'Heading for Title Test 2'),
56 ('to', '^----$')])
57 ]
58
59 import re
60
61 _arg_heading = r'(?P<heading>,)\s*(|(?P<hquote>[\'"])(?P<htext>.+?)(?P=hquote))'
62 OLD_arg_level = r',\s*(?P<level>\d+)'
63 _arg_level = r',\s*(?P<level>\d+)?'
64 _arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>.+?)(?P=fquote))?'
65 _arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>.+?)(?P=tquote))?'
66 _arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
67 _arg_items = r'(,\s*items=(?P<items>\d+))?'
68 _arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
69 _arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
70 _args_re_pattern_OLD = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s)?$' % (
71 _arg_heading, OLD_arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
72 _arg_skipitems, _arg_titlesonly)
73 _args_re_pattern = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s)?$' % (
74 _arg_heading, _arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
75 _arg_skipitems, _arg_titlesonly)
76
77 def test_import_re(pattern=_args_re_pattern):
78 args_re=re.compile(pattern)
79 test_num=0
80 number_failed=0
81 for test_set in test_sets:
82 test_failed=0
83 test_str = test_set['test']
84 test_num=test_num+1
85 print "Test", test_num, ":", test_str
86
87 args = args_re.match(test_str)
88 if not args:
89 print " FAIL: failed to match on test string"
90 test_failed=1
91 else:
92 for k, v in test_set.iteritems():
93 if k != 'test' and k != 'hquote':
94 re_res = args.group(k)
95 if not re_res:
96 print " FAIL: failed to match on key '", k, "'"
97 test_failed=1
98 elif v != re_res:
99 print " FAIL: On key '", k, "', value was '", re_res, "', expected '", v, "'."
100 test_failed=1
101 number_failed += test_failed
102
103 if number_failed == 0:
104 print "All tests passed!"
105 else:
106 print str(number_failed), "out of", str(test_num), "tests failed."
107
108 print "*** ORIGINAL CODE ***"
109 test_import_re(_args_re_pattern_OLD)
110
111 print
112 print "*** CHANGED CODE ***"
113 test_import_re()
Output is:
*** ORIGINAL CODE *** Test 1 : FooBar Test 2 : FooBar, Test 3 : FooBar, , 2 Test 4 : FooBar, 'All about Foo Bar', 2 Test 5 : FooBar, , from="^----$" Test 6 : FooBar, , to="^----$" Test 7 : ^FooBar/.*, , sort=descending Test 8 : ^FooBar/.*, , items=3 Test 9 : ^BlogBase/.*,, to="^----$", sort=descending, items=7 Test 10 : ^BlogBase/.*,, to="^----$", sort=descending, items=7, skipitems=7,titlesonly Test 11 : ^FirstnameLastname/20..-..-..,,to="^----",sort=descending,items=3 Test 12 : ^FirstnameLastname/20..-..-..,,to="^----",sort=descending,items=4,skipitems=3,titlesonly Test 13 : TitleTest, "Heading for Title Test 1",1,to="^----$" Test 14 : TitleTest, "Heading for Title Test 2", ,to="^----$" FAIL: On key ' htext ', value was ' Heading for Title Test 2", ,to="^----$ ', expected ' Heading for Title Test 2 '. FAIL: failed to match on key ' to ' 1 out of 14 tests failed. *** CHANGED CODE *** Test 1 : FooBar Test 2 : FooBar, Test 3 : FooBar, , 2 Test 4 : FooBar, 'All about Foo Bar', 2 Test 5 : FooBar, , from="^----$" Test 6 : FooBar, , to="^----$" Test 7 : ^FooBar/.*, , sort=descending Test 8 : ^FooBar/.*, , items=3 Test 9 : ^BlogBase/.*,, to="^----$", sort=descending, items=7 Test 10 : ^BlogBase/.*,, to="^----$", sort=descending, items=7, skipitems=7,titlesonly Test 11 : ^FirstnameLastname/20..-..-..,,to="^----",sort=descending,items=3 Test 12 : ^FirstnameLastname/20..-..-..,,to="^----",sort=descending,items=4,skipitems=3,titlesonly Test 13 : TitleTest, "Heading for Title Test 1",1,to="^----$" Test 14 : TitleTest, "Heading for Title Test 2", ,to="^----$" All tests passed!
I'd appreciate if someone could add this one-line fix to the development code, as it will take me a few more weekends to figure out how to make an official diff -- -- JohnWhitlock 2004-08-28 04:47:00
Do you have test code for this complext test code?
Test code should be very very simple, and test only one thing for each test. Use MoinMoin unittests framework and create simple test for each case. -- NirSoffer 2004-10-21 11:36:51
I will make an fix and check it against the docs. And I will add something to _tests. I think it would be an good idea to recommend the keyword parsing utils of moin to macro and parser writers... -- OliverGraf 2004-10-21 12:44:45
Plan
maybe this should be checked before 1.3 is out of beta
- Priority: low
Assigned to: OliverGraf
- Status: fixed long time ago