Merge ~cjwatson/launchpad:bs4-feeds into launchpad:master
- Git
- lp:~cjwatson/launchpad
- bs4-feeds
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 3535948a45d3a6eb486436b61d7d4ff6459175e5 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:bs4-feeds |
Merge into: | launchpad:master |
Diff against target: |
1148 lines (+247/-223) 10 files modified
lib/lp/bugs/stories/feeds/xx-bug-atom.txt (+51/-42) lib/lp/bugs/stories/feeds/xx-bug-html.txt (+5/-4) lib/lp/code/stories/feeds/xx-branch-atom.txt (+29/-22) lib/lp/code/stories/feeds/xx-revision-atom.txt (+9/-9) lib/lp/registry/stories/announcements/xx-announcements.txt (+13/-15) lib/lp/services/feeds/doc/feeds.txt (+1/-1) lib/lp/services/feeds/feed.py (+2/-4) lib/lp/services/feeds/stories/xx-links.txt (+109/-98) lib/lp/services/feeds/stories/xx-security.txt (+19/-19) lib/lp/services/feeds/tests/helper.py (+9/-9) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+377977@code.launchpad.net |
Commit message
Port feed tests to Beautiful Soup 4
Description of the change
To post a comment you must log in.
~cjwatson/launchpad:bs4-feeds
updated
- 3535948... by Colin Watson
-
Port xx-announcement
s.txt too It uses lp.services.
feeds.tests. helper, so needs to be ported along with
the other feed tests.By default, Beautiful Soup 4 produces output with HTML entities
converted to Unicode characters. I could have used formatter="html" or
similar to restore something closer to the old behaviour in this case,
but the new behaviour is easier to read.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/bugs/stories/feeds/xx-bug-atom.txt b/lib/lp/bugs/stories/feeds/xx-bug-atom.txt | |||
2 | index 4b0faa4..840b943 100644 | |||
3 | --- a/lib/lp/bugs/stories/feeds/xx-bug-atom.txt | |||
4 | +++ b/lib/lp/bugs/stories/feeds/xx-bug-atom.txt | |||
5 | @@ -1,10 +1,12 @@ | |||
6 | 1 | = Atom Feeds = | 1 | = Atom Feeds = |
7 | 2 | 2 | ||
8 | 3 | Atom feeds produce XML not HTML. Therefore we must parse the output as XML | 3 | Atom feeds produce XML not HTML. Therefore we must parse the output as XML |
10 | 4 | using BeautifulStoneSoup instead of BSS or the helper functions. | 4 | by asking BeautifulSoup to use lxml. |
11 | 5 | 5 | ||
14 | 6 | >>> from BeautifulSoup import BeautifulStoneSoup as BSS | 6 | >>> from lp.services.beautifulsoup import ( |
15 | 7 | >>> from BeautifulSoup import SoupStrainer | 7 | ... BeautifulSoup4 as BeautifulSoup, |
16 | 8 | ... SoupStrainer4 as SoupStrainer, | ||
17 | 9 | ... ) | ||
18 | 8 | >>> from lp.services.feeds.tests.helper import ( | 10 | >>> from lp.services.feeds.tests.helper import ( |
19 | 9 | ... parse_entries, parse_links, validate_feed) | 11 | ... parse_entries, parse_links, validate_feed) |
20 | 10 | 12 | ||
21 | @@ -26,25 +28,26 @@ point to the bugs themselves. | |||
22 | 26 | >>> validate_feed(browser.contents, | 28 | >>> validate_feed(browser.contents, |
23 | 27 | ... browser.headers['content-type'], browser.url) | 29 | ... browser.headers['content-type'], browser.url) |
24 | 28 | No Errors | 30 | No Errors |
26 | 29 | >>> BSS(browser.contents).title.contents | 31 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
27 | 30 | [u'Bugs in Jokosher'] | 32 | [u'Bugs in Jokosher'] |
28 | 31 | >>> browser.url | 33 | >>> browser.url |
29 | 32 | 'http://feeds.launchpad.test/jokosher/latest-bugs.atom' | 34 | 'http://feeds.launchpad.test/jokosher/latest-bugs.atom' |
30 | 33 | 35 | ||
32 | 34 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 36 | >>> soup = BeautifulSoup( |
33 | 37 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
34 | 35 | >>> print(extract_text(soup.find('id'))) | 38 | >>> print(extract_text(soup.find('id'))) |
35 | 36 | tag:launchpad.net,2007-03-15:/bugs/jokosher | 39 | tag:launchpad.net,2007-03-15:/bugs/jokosher |
36 | 37 | >>> alternate_links = parse_links(browser.contents, 'alternate') | 40 | >>> alternate_links = parse_links(browser.contents, 'alternate') |
37 | 38 | >>> for link in alternate_links: | 41 | >>> for link in alternate_links: |
38 | 39 | ... print(link) | 42 | ... print(link) |
42 | 40 | <link rel="alternate" href="http://bugs.launchpad.test/jokosher" /> | 43 | <link href="http://bugs.launchpad.test/jokosher" rel="alternate"/> |
43 | 41 | <link rel="alternate" href="http://bugs.launchpad.test/bugs/12" /> | 44 | <link href="http://bugs.launchpad.test/bugs/12" rel="alternate"/> |
44 | 42 | <link rel="alternate" href="http://bugs.launchpad.test/bugs/11" /> | 45 | <link href="http://bugs.launchpad.test/bugs/11" rel="alternate"/> |
45 | 43 | 46 | ||
46 | 44 | >>> self_links = parse_links(browser.contents, 'self') | 47 | >>> self_links = parse_links(browser.contents, 'self') |
47 | 45 | >>> for link in self_links: | 48 | >>> for link in self_links: |
48 | 46 | ... print(link) | 49 | ... print(link) |
50 | 47 | <link rel="self" href="http://feeds.launchpad.test/jokosher/latest-bugs.atom" /> | 50 | <link href="http://feeds.launchpad.test/jokosher/latest-bugs.atom" rel="self"/> |
51 | 48 | 51 | ||
52 | 49 | >>> entries = parse_entries(browser.contents) | 52 | >>> entries = parse_entries(browser.contents) |
53 | 50 | >>> print(len(entries)) | 53 | >>> print(len(entries)) |
54 | @@ -83,19 +86,20 @@ as the latest bugs feed for a product. | |||
55 | 83 | >>> validate_feed(browser.contents, | 86 | >>> validate_feed(browser.contents, |
56 | 84 | ... browser.headers['content-type'], browser.url) | 87 | ... browser.headers['content-type'], browser.url) |
57 | 85 | No Errors | 88 | No Errors |
59 | 86 | >>> BSS(browser.contents).title.contents | 89 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
60 | 87 | [u'Bugs in The Mozilla Project'] | 90 | [u'Bugs in The Mozilla Project'] |
61 | 88 | >>> browser.url | 91 | >>> browser.url |
62 | 89 | 'http://feeds.launchpad.test/mozilla/latest-bugs.atom' | 92 | 'http://feeds.launchpad.test/mozilla/latest-bugs.atom' |
63 | 90 | 93 | ||
65 | 91 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 94 | >>> soup = BeautifulSoup( |
66 | 95 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
67 | 92 | >>> print(extract_text(soup.find('id'))) | 96 | >>> print(extract_text(soup.find('id'))) |
68 | 93 | tag:launchpad.net,2004-09-24:/bugs/mozilla | 97 | tag:launchpad.net,2004-09-24:/bugs/mozilla |
69 | 94 | 98 | ||
70 | 95 | >>> self_links = parse_links(browser.contents, 'self') | 99 | >>> self_links = parse_links(browser.contents, 'self') |
71 | 96 | >>> for link in self_links: | 100 | >>> for link in self_links: |
72 | 97 | ... print(link) | 101 | ... print(link) |
74 | 98 | <link rel="self" href="http://feeds.launchpad.test/mozilla/latest-bugs.atom" /> | 102 | <link href="http://feeds.launchpad.test/mozilla/latest-bugs.atom" rel="self"/> |
75 | 99 | 103 | ||
76 | 100 | >>> entries = parse_entries(browser.contents) | 104 | >>> entries = parse_entries(browser.contents) |
77 | 101 | >>> print(len(entries)) | 105 | >>> print(len(entries)) |
78 | @@ -144,19 +148,20 @@ of content as the latest bugs feed for a product. | |||
79 | 144 | >>> validate_feed(browser.contents, | 148 | >>> validate_feed(browser.contents, |
80 | 145 | ... browser.headers['content-type'], browser.url) | 149 | ... browser.headers['content-type'], browser.url) |
81 | 146 | No Errors | 150 | No Errors |
83 | 147 | >>> BSS(browser.contents).title.contents | 151 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
84 | 148 | [u'Bugs in Ubuntu'] | 152 | [u'Bugs in Ubuntu'] |
85 | 149 | >>> browser.url | 153 | >>> browser.url |
86 | 150 | 'http://feeds.launchpad.test/ubuntu/latest-bugs.atom' | 154 | 'http://feeds.launchpad.test/ubuntu/latest-bugs.atom' |
87 | 151 | 155 | ||
89 | 152 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 156 | >>> soup = BeautifulSoup( |
90 | 157 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
91 | 153 | >>> print(extract_text(soup.find('id'))) | 158 | >>> print(extract_text(soup.find('id'))) |
92 | 154 | tag:launchpad.net,2006-10-16:/bugs/ubuntu | 159 | tag:launchpad.net,2006-10-16:/bugs/ubuntu |
93 | 155 | 160 | ||
94 | 156 | >>> self_links = parse_links(browser.contents, 'self') | 161 | >>> self_links = parse_links(browser.contents, 'self') |
95 | 157 | >>> for link in self_links: | 162 | >>> for link in self_links: |
96 | 158 | ... print(link) | 163 | ... print(link) |
98 | 159 | <link rel="self" href="http://feeds.launchpad.test/ubuntu/latest-bugs.atom" /> | 164 | <link href="http://feeds.launchpad.test/ubuntu/latest-bugs.atom" rel="self"/> |
99 | 160 | 165 | ||
100 | 161 | >>> entries = parse_entries(browser.contents) | 166 | >>> entries = parse_entries(browser.contents) |
101 | 162 | >>> print(len(entries)) | 167 | >>> print(len(entries)) |
102 | @@ -214,11 +219,8 @@ The bug should be included in the feed. | |||
103 | 214 | 219 | ||
104 | 215 | Private teams should show as '-'. | 220 | Private teams should show as '-'. |
105 | 216 | 221 | ||
111 | 217 | >>> entry_content = BSS( | 222 | >>> soup = BeautifulSoup(entry.find('content').text, 'xml') |
112 | 218 | ... entry.find('content').text, | 223 | >>> print([tr.find_all('td')[4].text for tr in soup.find_all('tr')[1:4]]) |
108 | 219 | ... convertEntities=BSS.HTML_ENTITIES) | ||
109 | 220 | >>> soup = BSS(entry_content.text) | ||
110 | 221 | >>> print([tr.findAll('td')[4].text for tr in soup.findAll('tr')[1:4]]) | ||
113 | 222 | [u'Mark Shuttleworth', u'-', u'-'] | 224 | [u'Mark Shuttleworth', u'-', u'-'] |
114 | 223 | 225 | ||
115 | 224 | == Latest bugs for a source package == | 226 | == Latest bugs for a source package == |
116 | @@ -232,11 +234,12 @@ type of content as the latest bugs feed for a product. | |||
117 | 232 | >>> validate_feed(browser.contents, | 234 | >>> validate_feed(browser.contents, |
118 | 233 | ... browser.headers['content-type'], browser.url) | 235 | ... browser.headers['content-type'], browser.url) |
119 | 234 | No Errors | 236 | No Errors |
121 | 235 | >>> BSS(browser.contents).title.contents | 237 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
122 | 236 | [u'Bugs in thunderbird in Ubuntu'] | 238 | [u'Bugs in thunderbird in Ubuntu'] |
123 | 237 | >>> browser.url | 239 | >>> browser.url |
124 | 238 | 'http://feeds.launchpad.test/ubuntu/+source/thunderbird/latest-bugs.atom' | 240 | 'http://feeds.launchpad.test/ubuntu/+source/thunderbird/latest-bugs.atom' |
126 | 239 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 241 | >>> soup = BeautifulSoup( |
127 | 242 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
128 | 240 | >>> print(extract_text(soup.find('id'))) | 243 | >>> print(extract_text(soup.find('id'))) |
129 | 241 | tag:launchpad.net,2008:/bugs/ubuntu/+source/thunderbird | 244 | tag:launchpad.net,2008:/bugs/ubuntu/+source/thunderbird |
130 | 242 | >>> entries = parse_entries(browser.contents) | 245 | >>> entries = parse_entries(browser.contents) |
131 | @@ -264,19 +267,20 @@ type of content as the latest bugs feed for a product. | |||
132 | 264 | >>> validate_feed(browser.contents, | 267 | >>> validate_feed(browser.contents, |
133 | 265 | ... browser.headers['content-type'], browser.url) | 268 | ... browser.headers['content-type'], browser.url) |
134 | 266 | No Errors | 269 | No Errors |
136 | 267 | >>> BSS(browser.contents).title.contents | 270 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
137 | 268 | [u'Bugs in Hoary'] | 271 | [u'Bugs in Hoary'] |
138 | 269 | >>> browser.url | 272 | >>> browser.url |
139 | 270 | 'http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom' | 273 | 'http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom' |
140 | 271 | 274 | ||
142 | 272 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 275 | >>> soup = BeautifulSoup( |
143 | 276 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
144 | 273 | >>> print(extract_text(soup.find('id'))) | 277 | >>> print(extract_text(soup.find('id'))) |
145 | 274 | tag:launchpad.net,2006-10-16:/bugs/ubuntu/hoary | 278 | tag:launchpad.net,2006-10-16:/bugs/ubuntu/hoary |
146 | 275 | 279 | ||
147 | 276 | >>> self_links = parse_links(browser.contents, 'self') | 280 | >>> self_links = parse_links(browser.contents, 'self') |
148 | 277 | >>> for link in self_links: | 281 | >>> for link in self_links: |
149 | 278 | ... print(link) | 282 | ... print(link) |
151 | 279 | <link rel="self" href="http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom" /> | 283 | <link href="http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom" rel="self"/> |
152 | 280 | 284 | ||
153 | 281 | >>> entries = parse_entries(browser.contents) | 285 | >>> entries = parse_entries(browser.contents) |
154 | 282 | >>> print(len(entries)) | 286 | >>> print(len(entries)) |
155 | @@ -304,19 +308,20 @@ type of content as the latest bugs feed for a product. | |||
156 | 304 | >>> validate_feed(browser.contents, | 308 | >>> validate_feed(browser.contents, |
157 | 305 | ... browser.headers['content-type'], browser.url) | 309 | ... browser.headers['content-type'], browser.url) |
158 | 306 | No Errors | 310 | No Errors |
160 | 307 | >>> BSS(browser.contents).title.contents | 311 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
161 | 308 | [u'Bugs in 1.0'] | 312 | [u'Bugs in 1.0'] |
162 | 309 | >>> browser.url | 313 | >>> browser.url |
163 | 310 | 'http://feeds.launchpad.test/firefox/1.0/latest-bugs.atom' | 314 | 'http://feeds.launchpad.test/firefox/1.0/latest-bugs.atom' |
164 | 311 | 315 | ||
166 | 312 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 316 | >>> soup = BeautifulSoup( |
167 | 317 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
168 | 313 | >>> print(extract_text(soup.find('id'))) | 318 | >>> print(extract_text(soup.find('id'))) |
169 | 314 | tag:launchpad.net,2005-06-06:/bugs/firefox/1.0 | 319 | tag:launchpad.net,2005-06-06:/bugs/firefox/1.0 |
170 | 315 | 320 | ||
171 | 316 | >>> self_links = parse_links(browser.contents, 'self') | 321 | >>> self_links = parse_links(browser.contents, 'self') |
172 | 317 | >>> for link in self_links: | 322 | >>> for link in self_links: |
173 | 318 | ... print(link) | 323 | ... print(link) |
175 | 319 | <link rel="self" href="http://feeds.launchpad.test/firefox/1.0/latest-bugs.atom" /> | 324 | <link href="http://feeds.launchpad.test/firefox/1.0/latest-bugs.atom" rel="self"/> |
176 | 320 | 325 | ||
177 | 321 | >>> entries = parse_entries(browser.contents) | 326 | >>> entries = parse_entries(browser.contents) |
178 | 322 | >>> print(len(entries)) | 327 | >>> print(len(entries)) |
179 | @@ -342,19 +347,20 @@ This feed gets the latest bugs for a person. | |||
180 | 342 | >>> validate_feed(browser.contents, | 347 | >>> validate_feed(browser.contents, |
181 | 343 | ... browser.headers['content-type'], browser.url) | 348 | ... browser.headers['content-type'], browser.url) |
182 | 344 | No Errors | 349 | No Errors |
184 | 345 | >>> BSS(browser.contents).title.contents | 350 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
185 | 346 | [u'Bugs for Foo Bar'] | 351 | [u'Bugs for Foo Bar'] |
186 | 347 | >>> browser.url | 352 | >>> browser.url |
187 | 348 | 'http://feeds.launchpad.test/~name16/latest-bugs.atom' | 353 | 'http://feeds.launchpad.test/~name16/latest-bugs.atom' |
188 | 349 | 354 | ||
190 | 350 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 355 | >>> soup = BeautifulSoup( |
191 | 356 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
192 | 351 | >>> print(extract_text(soup.find('id'))) | 357 | >>> print(extract_text(soup.find('id'))) |
193 | 352 | tag:launchpad.net,2005-06-06:/bugs/~name16 | 358 | tag:launchpad.net,2005-06-06:/bugs/~name16 |
194 | 353 | 359 | ||
195 | 354 | >>> self_links = parse_links(browser.contents, 'self') | 360 | >>> self_links = parse_links(browser.contents, 'self') |
196 | 355 | >>> for link in self_links: | 361 | >>> for link in self_links: |
197 | 356 | ... print(link) | 362 | ... print(link) |
199 | 357 | <link rel="self" href="http://feeds.launchpad.test/~name16/latest-bugs.atom" /> | 363 | <link href="http://feeds.launchpad.test/~name16/latest-bugs.atom" rel="self"/> |
200 | 358 | 364 | ||
201 | 359 | >>> entries = parse_entries(browser.contents) | 365 | >>> entries = parse_entries(browser.contents) |
202 | 360 | >>> print(len(entries)) | 366 | >>> print(len(entries)) |
203 | @@ -417,17 +423,18 @@ some results. | |||
204 | 417 | >>> validate_feed(browser.contents, | 423 | >>> validate_feed(browser.contents, |
205 | 418 | ... browser.headers['content-type'], browser.url) | 424 | ... browser.headers['content-type'], browser.url) |
206 | 419 | No Errors | 425 | No Errors |
208 | 420 | >>> BSS(browser.contents).title.contents | 426 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
209 | 421 | [u'Bugs for Simple Team'] | 427 | [u'Bugs for Simple Team'] |
210 | 422 | 428 | ||
212 | 423 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 429 | >>> soup = BeautifulSoup( |
213 | 430 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
214 | 424 | >>> print(extract_text(soup.find('id'))) | 431 | >>> print(extract_text(soup.find('id'))) |
215 | 425 | tag:launchpad.net,2007-02-21:/bugs/~simple-team | 432 | tag:launchpad.net,2007-02-21:/bugs/~simple-team |
216 | 426 | 433 | ||
217 | 427 | >>> self_links = parse_links(browser.contents, 'self') | 434 | >>> self_links = parse_links(browser.contents, 'self') |
218 | 428 | >>> for link in self_links: | 435 | >>> for link in self_links: |
219 | 429 | ... print(link) | 436 | ... print(link) |
221 | 430 | <link rel="self" href="http://feeds.launchpad.test/~simple-team/latest-bugs.atom" /> | 437 | <link href="http://feeds.launchpad.test/~simple-team/latest-bugs.atom" rel="self"/> |
222 | 431 | 438 | ||
223 | 432 | >>> entries = parse_entries(browser.contents) | 439 | >>> entries = parse_entries(browser.contents) |
224 | 433 | >>> print(len(entries)) | 440 | >>> print(len(entries)) |
225 | @@ -445,19 +452,20 @@ This feed gets the latest bugs reported against any target. | |||
226 | 445 | >>> validate_feed(browser.contents, | 452 | >>> validate_feed(browser.contents, |
227 | 446 | ... browser.headers['content-type'], browser.url) | 453 | ... browser.headers['content-type'], browser.url) |
228 | 447 | No Errors | 454 | No Errors |
230 | 448 | >>> BSS(browser.contents).title.contents | 455 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
231 | 449 | [u'Launchpad bugs'] | 456 | [u'Launchpad bugs'] |
232 | 450 | >>> browser.url | 457 | >>> browser.url |
233 | 451 | 'http://feeds.launchpad.test/bugs/latest-bugs.atom' | 458 | 'http://feeds.launchpad.test/bugs/latest-bugs.atom' |
234 | 452 | 459 | ||
236 | 453 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 460 | >>> soup = BeautifulSoup( |
237 | 461 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
238 | 454 | >>> print(extract_text(soup.find('id'))) | 462 | >>> print(extract_text(soup.find('id'))) |
239 | 455 | tag:launchpad.net,2008:/bugs | 463 | tag:launchpad.net,2008:/bugs |
240 | 456 | 464 | ||
241 | 457 | >>> self_links = parse_links(browser.contents, 'self') | 465 | >>> self_links = parse_links(browser.contents, 'self') |
242 | 458 | >>> for link in self_links: | 466 | >>> for link in self_links: |
243 | 459 | ... print(link) | 467 | ... print(link) |
245 | 460 | <link rel="self" href="http://feeds.launchpad.test/bugs/latest-bugs.atom" /> | 468 | <link href="http://feeds.launchpad.test/bugs/latest-bugs.atom" rel="self"/> |
246 | 461 | 469 | ||
247 | 462 | >>> entries = parse_entries(browser.contents) | 470 | >>> entries = parse_entries(browser.contents) |
248 | 463 | >>> print(len(entries)) | 471 | >>> print(len(entries)) |
249 | @@ -508,10 +516,11 @@ The bug search feed can be tested after setting is_bug_search_feed_active | |||
250 | 508 | to True. | 516 | to True. |
251 | 509 | 517 | ||
252 | 510 | >>> browser.open(url) | 518 | >>> browser.open(url) |
254 | 511 | >>> BSS(browser.contents).title.contents | 519 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
255 | 512 | [u'Bugs from custom search'] | 520 | [u'Bugs from custom search'] |
256 | 513 | 521 | ||
258 | 514 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 522 | >>> soup = BeautifulSoup( |
259 | 523 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
260 | 515 | >>> feed_id = extract_text(soup.find('id')) | 524 | >>> feed_id = extract_text(soup.find('id')) |
261 | 516 | >>> print(feed_id) | 525 | >>> print(feed_id) |
262 | 517 | tag:launchpad.net,2008:/+bugs.atom?field.scope.target=&field.scope=all&field.searchtext=&search=Search+Bug+Reports | 526 | tag:launchpad.net,2008:/+bugs.atom?field.scope.target=&field.scope=all&field.searchtext=&search=Search+Bug+Reports |
263 | @@ -523,7 +532,7 @@ to True. | |||
264 | 523 | >>> self_links = parse_links(browser.contents, 'self') | 532 | >>> self_links = parse_links(browser.contents, 'self') |
265 | 524 | >>> for link in self_links: | 533 | >>> for link in self_links: |
266 | 525 | ... print(link) | 534 | ... print(link) |
268 | 526 | <link rel="self" href="http://feeds.launchpad.test/bugs/+bugs.atom?field.scope.target=&field.scope=all&field.searchtext=&search=Search+Bug+Reports" /> | 535 | <link href="http://feeds.launchpad.test/bugs/+bugs.atom?field.scope.target=&field.scope=all&field.searchtext=&search=Search+Bug+Reports" rel="self"/> |
269 | 527 | 536 | ||
270 | 528 | >>> entries = parse_entries(browser.contents) | 537 | >>> entries = parse_entries(browser.contents) |
271 | 529 | >>> print(len(entries)) | 538 | >>> print(len(entries)) |
272 | @@ -554,7 +563,7 @@ This feed shows the status of a single bug. | |||
273 | 554 | >>> validate_feed(browser.contents, | 563 | >>> validate_feed(browser.contents, |
274 | 555 | ... browser.headers['content-type'], browser.url) | 564 | ... browser.headers['content-type'], browser.url) |
275 | 556 | No Errors | 565 | No Errors |
277 | 557 | >>> BSS(browser.contents).title.contents | 566 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
278 | 558 | [u'Bug 1'] | 567 | [u'Bug 1'] |
279 | 559 | >>> entries = parse_entries(browser.contents) | 568 | >>> entries = parse_entries(browser.contents) |
280 | 560 | >>> print(len(entries)) | 569 | >>> print(len(entries)) |
281 | @@ -565,7 +574,7 @@ This feed shows the status of a single bug. | |||
282 | 565 | >>> self_links = parse_links(browser.contents, 'self') | 574 | >>> self_links = parse_links(browser.contents, 'self') |
283 | 566 | >>> for link in self_links: | 575 | >>> for link in self_links: |
284 | 567 | ... print(link) | 576 | ... print(link) |
286 | 568 | <link rel="self" href="http://feeds.launchpad.test/bugs/1/bug.atom" /> | 577 | <link href="http://feeds.launchpad.test/bugs/1/bug.atom" rel="self"/> |
287 | 569 | 578 | ||
288 | 570 | == Feeds Configuration Options == | 579 | == Feeds Configuration Options == |
289 | 571 | 580 | ||
290 | diff --git a/lib/lp/bugs/stories/feeds/xx-bug-html.txt b/lib/lp/bugs/stories/feeds/xx-bug-html.txt | |||
291 | index fff1f4f..fa02653 100644 | |||
292 | --- a/lib/lp/bugs/stories/feeds/xx-bug-html.txt | |||
293 | +++ b/lib/lp/bugs/stories/feeds/xx-bug-html.txt | |||
294 | @@ -5,15 +5,16 @@ The content of an HTML feed is very similar to an Atom feed, but is formatted | |||
295 | 5 | as HTML instead of Atom. | 5 | as HTML instead of Atom. |
296 | 6 | 6 | ||
297 | 7 | >>> from lp.services.beautifulsoup import ( | 7 | >>> from lp.services.beautifulsoup import ( |
300 | 8 | ... BeautifulSoup, | 8 | ... BeautifulSoup4 as BeautifulSoup, |
301 | 9 | ... SoupStrainer, | 9 | ... SoupStrainer4 as SoupStrainer, |
302 | 10 | ... ) | 10 | ... ) |
303 | 11 | 11 | ||
304 | 12 | Define a helper function for parsing the entries: | 12 | Define a helper function for parsing the entries: |
305 | 13 | 13 | ||
306 | 14 | >>> def parse_entries(contents): | 14 | >>> def parse_entries(contents): |
309 | 15 | ... entries = [tag for tag in BeautifulSoup(browser.contents, | 15 | ... entries = [ |
310 | 16 | ... parseOnlyThese=SoupStrainer('tr'))] | 16 | ... tag for tag in BeautifulSoup( |
311 | 17 | ... browser.contents, parse_only=SoupStrainer('tr'))] | ||
312 | 17 | ... return entries | 18 | ... return entries |
313 | 18 | 19 | ||
314 | 19 | And two for printing the results: | 20 | And two for printing the results: |
315 | diff --git a/lib/lp/code/stories/feeds/xx-branch-atom.txt b/lib/lp/code/stories/feeds/xx-branch-atom.txt | |||
316 | index db90f8f..74db8e1 100644 | |||
317 | --- a/lib/lp/code/stories/feeds/xx-branch-atom.txt | |||
318 | +++ b/lib/lp/code/stories/feeds/xx-branch-atom.txt | |||
319 | @@ -1,10 +1,12 @@ | |||
320 | 1 | = Atom Feeds For Branches = | 1 | = Atom Feeds For Branches = |
321 | 2 | 2 | ||
322 | 3 | Atom feeds produce XML not HTML. Therefore we must parse the output as XML | 3 | Atom feeds produce XML not HTML. Therefore we must parse the output as XML |
324 | 4 | using BeautifulStoneSoup instead of BeautifulSoup or the helper functions. | 4 | by asking BeautifulSoup to use lxml. |
325 | 5 | 5 | ||
328 | 6 | >>> from BeautifulSoup import BeautifulStoneSoup as BSS | 6 | >>> from lp.services.beautifulsoup import ( |
329 | 7 | >>> from BeautifulSoup import SoupStrainer | 7 | ... BeautifulSoup4 as BeautifulSoup, |
330 | 8 | ... SoupStrainer4 as SoupStrainer, | ||
331 | 9 | ... ) | ||
332 | 8 | >>> from lp.services.feeds.tests.helper import ( | 10 | >>> from lp.services.feeds.tests.helper import ( |
333 | 9 | ... parse_ids, parse_links, validate_feed) | 11 | ... parse_ids, parse_links, validate_feed) |
334 | 10 | 12 | ||
335 | @@ -49,7 +51,7 @@ which will include an entry for each branch. | |||
336 | 49 | ... browser.contents, browser.headers['content-type'], browser.url) | 51 | ... browser.contents, browser.headers['content-type'], browser.url) |
337 | 50 | >>> validate_browser_feed(anon_browser) | 52 | >>> validate_browser_feed(anon_browser) |
338 | 51 | No Errors | 53 | No Errors |
340 | 52 | >>> BSS(anon_browser.contents).title.contents | 54 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
341 | 53 | [u'Branches for Mike Murphy'] | 55 | [u'Branches for Mike Murphy'] |
342 | 54 | >>> def print_parse_ids(browser): | 56 | >>> def print_parse_ids(browser): |
343 | 55 | ... for id in parse_ids(browser.contents): | 57 | ... for id in parse_ids(browser.contents): |
344 | @@ -71,14 +73,15 @@ Ensure the self link is correct and there is only one. | |||
345 | 71 | ... for link in parse_links(browser.contents, rel="self"): | 73 | ... for link in parse_links(browser.contents, rel="self"): |
346 | 72 | ... print(link) | 74 | ... print(link) |
347 | 73 | >>> print_parse_links(anon_browser) | 75 | >>> print_parse_links(anon_browser) |
349 | 74 | <link rel="self" href="http://feeds.launchpad.test/~mike/branches.atom" /> | 76 | <link href="http://feeds.launchpad.test/~mike/branches.atom" rel="self"/> |
350 | 75 | 77 | ||
351 | 76 | The <update> field for the feed will be the most recent value for the | 78 | The <update> field for the feed will be the most recent value for the |
352 | 77 | updated field in all of the entries. | 79 | updated field in all of the entries. |
353 | 78 | 80 | ||
354 | 79 | >>> strainer = SoupStrainer('updated') | 81 | >>> strainer = SoupStrainer('updated') |
357 | 80 | >>> updated_dates = [extract_text(tag) for tag in BSS(anon_browser.contents, | 82 | >>> updated_dates = [ |
358 | 81 | ... parseOnlyThese=strainer)] | 83 | ... extract_text(tag) for tag in BeautifulSoup( |
359 | 84 | ... anon_browser.contents, 'xml', parse_only=strainer)] | ||
360 | 82 | >>> feed_updated = updated_dates[0] | 85 | >>> feed_updated = updated_dates[0] |
361 | 83 | >>> entry_dates = sorted(updated_dates[1:], reverse=True) | 86 | >>> entry_dates = sorted(updated_dates[1:], reverse=True) |
362 | 84 | >>> assert feed_updated == entry_dates[0], ( | 87 | >>> assert feed_updated == entry_dates[0], ( |
363 | @@ -90,7 +93,7 @@ still be hidden: | |||
364 | 90 | >>> anon_browser.open('http://feeds.launchpad.test/~name12/branches.atom') | 93 | >>> anon_browser.open('http://feeds.launchpad.test/~name12/branches.atom') |
365 | 91 | >>> validate_browser_feed(anon_browser) | 94 | >>> validate_browser_feed(anon_browser) |
366 | 92 | No Errors | 95 | No Errors |
368 | 93 | >>> BSS(anon_browser.contents).title.contents | 96 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
369 | 94 | [u'Branches for Sample Person'] | 97 | [u'Branches for Sample Person'] |
370 | 95 | >>> 'foo@localhost' in anon_browser.contents | 98 | >>> 'foo@localhost' in anon_browser.contents |
371 | 96 | False | 99 | False |
372 | @@ -125,7 +128,7 @@ branches listed, just an id for the feed. | |||
373 | 125 | >>> browser.open('http://feeds.launchpad.test/~landscape-developers/branches.atom') | 128 | >>> browser.open('http://feeds.launchpad.test/~landscape-developers/branches.atom') |
374 | 126 | >>> validate_browser_feed(browser) | 129 | >>> validate_browser_feed(browser) |
375 | 127 | No Errors | 130 | No Errors |
377 | 128 | >>> BSS(browser.contents).title.contents | 131 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
378 | 129 | [u'Branches for Landscape Developers'] | 132 | [u'Branches for Landscape Developers'] |
379 | 130 | >>> print_parse_ids(browser) | 133 | >>> print_parse_ids(browser) |
380 | 131 | <id>tag:launchpad.net,2006-07-11:/code/~landscape-developers</id> | 134 | <id>tag:launchpad.net,2006-07-11:/code/~landscape-developers</id> |
381 | @@ -139,7 +142,7 @@ which will include an entry for each branch. | |||
382 | 139 | >>> anon_browser.open('http://feeds.launchpad.test/fooix/branches.atom') | 142 | >>> anon_browser.open('http://feeds.launchpad.test/fooix/branches.atom') |
383 | 140 | >>> validate_browser_feed(anon_browser) | 143 | >>> validate_browser_feed(anon_browser) |
384 | 141 | No Errors | 144 | No Errors |
386 | 142 | >>> BSS(anon_browser.contents).title.contents | 145 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
387 | 143 | [u'Branches for Fooix'] | 146 | [u'Branches for Fooix'] |
388 | 144 | >>> print_parse_ids(anon_browser) | 147 | >>> print_parse_ids(anon_browser) |
389 | 145 | <id>tag:launchpad.net,...:/code/fooix</id> | 148 | <id>tag:launchpad.net,...:/code/fooix</id> |
390 | @@ -148,14 +151,15 @@ which will include an entry for each branch. | |||
391 | 148 | <id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id> | 151 | <id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id> |
392 | 149 | 152 | ||
393 | 150 | >>> print_parse_links(anon_browser) | 153 | >>> print_parse_links(anon_browser) |
395 | 151 | <link rel="self" href="http://feeds.launchpad.test/fooix/branches.atom" /> | 154 | <link href="http://feeds.launchpad.test/fooix/branches.atom" rel="self"/> |
396 | 152 | 155 | ||
397 | 153 | The <update> field for the feed will be the most recent value for the | 156 | The <update> field for the feed will be the most recent value for the |
398 | 154 | updated field in all of the entries. | 157 | updated field in all of the entries. |
399 | 155 | 158 | ||
400 | 156 | >>> strainer = SoupStrainer('updated') | 159 | >>> strainer = SoupStrainer('updated') |
403 | 157 | >>> updated_dates = [extract_text(tag) for tag in BSS(anon_browser.contents, | 160 | >>> updated_dates = [ |
404 | 158 | ... parseOnlyThese=strainer)] | 161 | ... extract_text(tag) for tag in BeautifulSoup( |
405 | 162 | ... anon_browser.contents, 'xml', parse_only=strainer)] | ||
406 | 159 | >>> feed_updated = updated_dates[0] | 163 | >>> feed_updated = updated_dates[0] |
407 | 160 | >>> entry_dates = sorted(updated_dates[1:], reverse=True) | 164 | >>> entry_dates = sorted(updated_dates[1:], reverse=True) |
408 | 161 | >>> assert feed_updated == entry_dates[0], ( | 165 | >>> assert feed_updated == entry_dates[0], ( |
409 | @@ -170,7 +174,7 @@ branches which will include an entry for each branch. | |||
410 | 170 | >>> anon_browser.open('http://feeds.launchpad.test/oh-man/branches.atom') | 174 | >>> anon_browser.open('http://feeds.launchpad.test/oh-man/branches.atom') |
411 | 171 | >>> validate_browser_feed(anon_browser) | 175 | >>> validate_browser_feed(anon_browser) |
412 | 172 | No Errors | 176 | No Errors |
414 | 173 | >>> BSS(anon_browser.contents).title.contents | 177 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
415 | 174 | [u'Branches for Oh Man'] | 178 | [u'Branches for Oh Man'] |
416 | 175 | >>> print_parse_ids(anon_browser) | 179 | >>> print_parse_ids(anon_browser) |
417 | 176 | <id>tag:launchpad.net,...:/code/oh-man</id> | 180 | <id>tag:launchpad.net,...:/code/oh-man</id> |
418 | @@ -182,14 +186,15 @@ branches which will include an entry for each branch. | |||
419 | 182 | <id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id> | 186 | <id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id> |
420 | 183 | 187 | ||
421 | 184 | >>> print_parse_links(anon_browser) | 188 | >>> print_parse_links(anon_browser) |
423 | 185 | <link rel="self" href="http://feeds.launchpad.test/oh-man/branches.atom" /> | 189 | <link href="http://feeds.launchpad.test/oh-man/branches.atom" rel="self"/> |
424 | 186 | 190 | ||
425 | 187 | The <update> field for the feed will be the most recent value for the | 191 | The <update> field for the feed will be the most recent value for the |
426 | 188 | updated field in all of the entries. | 192 | updated field in all of the entries. |
427 | 189 | 193 | ||
428 | 190 | >>> strainer = SoupStrainer('updated') | 194 | >>> strainer = SoupStrainer('updated') |
431 | 191 | >>> updated_dates = [extract_text(tag) for tag in BSS(anon_browser.contents, | 195 | >>> updated_dates = [ |
432 | 192 | ... parseOnlyThese=strainer)] | 196 | ... extract_text(tag) for tag in BeautifulSoup( |
433 | 197 | ... anon_browser.contents, 'xml', parse_only=strainer)] | ||
434 | 193 | >>> feed_updated = updated_dates[0] | 198 | >>> feed_updated = updated_dates[0] |
435 | 194 | >>> entry_dates = sorted(updated_dates[1:], reverse=True) | 199 | >>> entry_dates = sorted(updated_dates[1:], reverse=True) |
436 | 195 | >>> assert feed_updated == entry_dates[0], ( | 200 | >>> assert feed_updated == entry_dates[0], ( |
437 | @@ -206,7 +211,7 @@ different entry. | |||
438 | 206 | >>> validate_feed(browser.contents, | 211 | >>> validate_feed(browser.contents, |
439 | 207 | ... browser.headers['content-type'], browser.url) | 212 | ... browser.headers['content-type'], browser.url) |
440 | 208 | No Errors | 213 | No Errors |
442 | 209 | >>> BSS(browser.contents).title.contents | 214 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
443 | 210 | [u'Latest Revisions for Branch lp://dev/~mark/firefox/release--0.9.1'] | 215 | [u'Latest Revisions for Branch lp://dev/~mark/firefox/release--0.9.1'] |
444 | 211 | >>> print(browser.url) | 216 | >>> print(browser.url) |
445 | 212 | http://feeds.launchpad.test/~mark/firefox/release--0.9.1/branch.atom | 217 | http://feeds.launchpad.test/~mark/firefox/release--0.9.1/branch.atom |
446 | @@ -214,17 +219,19 @@ different entry. | |||
447 | 214 | The first <id> in a feed identifies the feed. Each entry then has its | 219 | The first <id> in a feed identifies the feed. Each entry then has its |
448 | 215 | own <id>, which in the case of a single branch feed will be identical. | 220 | own <id>, which in the case of a single branch feed will be identical. |
449 | 216 | 221 | ||
451 | 217 | >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id')) | 222 | >>> soup = BeautifulSoup( |
452 | 223 | ... browser.contents, 'xml', parse_only=SoupStrainer('id')) | ||
453 | 218 | >>> ids = parse_ids(browser.contents) | 224 | >>> ids = parse_ids(browser.contents) |
454 | 219 | >>> for id_ in ids: | 225 | >>> for id_ in ids: |
455 | 220 | ... print(id_) | 226 | ... print(id_) |
456 | 221 | <id>tag:launchpad.net,2006-10-16:/code/~mark/firefox/release--0.9.1</id> | 227 | <id>tag:launchpad.net,2006-10-16:/code/~mark/firefox/release--0.9.1</id> |
457 | 222 | <id>tag:launchpad.net,2005-03-09:/code/~mark/firefox/release--0.9.1/revision/1</id> | 228 | <id>tag:launchpad.net,2005-03-09:/code/~mark/firefox/release--0.9.1/revision/1</id> |
458 | 223 | >>> print_parse_links(browser) | 229 | >>> print_parse_links(browser) |
460 | 224 | <link rel="self" href="http://feeds.launchpad.test/~mark/firefox/release--0.9.1/branch.atom" /> | 230 | <link href="http://feeds.launchpad.test/~mark/firefox/release--0.9.1/branch.atom" rel="self"/> |
461 | 225 | >>> strainer = SoupStrainer('updated') | 231 | >>> strainer = SoupStrainer('updated') |
464 | 226 | >>> updated_dates = [extract_text(tag) for tag in BSS(browser.contents, | 232 | >>> updated_dates = [ |
465 | 227 | ... parseOnlyThese=strainer)] | 233 | ... extract_text(tag) for tag in BeautifulSoup( |
466 | 234 | ... browser.contents, 'xml', parse_only=strainer)] | ||
467 | 228 | 235 | ||
468 | 229 | The update date for the entire feed (updated_dates[0]) must be equal | 236 | The update date for the entire feed (updated_dates[0]) must be equal |
469 | 230 | to the update_date of the first entry in the feed (updated_dates[1]). | 237 | to the update_date of the first entry in the feed (updated_dates[1]). |
470 | diff --git a/lib/lp/code/stories/feeds/xx-revision-atom.txt b/lib/lp/code/stories/feeds/xx-revision-atom.txt | |||
471 | index 0ef5eae..3638b09 100644 | |||
472 | --- a/lib/lp/code/stories/feeds/xx-revision-atom.txt | |||
473 | +++ b/lib/lp/code/stories/feeds/xx-revision-atom.txt | |||
474 | @@ -1,9 +1,9 @@ | |||
475 | 1 | = Atom Feeds For Revisions = | 1 | = Atom Feeds For Revisions = |
476 | 2 | 2 | ||
477 | 3 | Atom feeds produce XML not HTML. Therefore we must parse the output as XML | 3 | Atom feeds produce XML not HTML. Therefore we must parse the output as XML |
479 | 4 | using BeautifulStoneSoup instead of BeautifulSoup or the helper functions. | 4 | by asking BeautifulSoup to use lxml. |
480 | 5 | 5 | ||
482 | 6 | >>> from BeautifulSoup import BeautifulStoneSoup as BSS | 6 | >>> from lp.services.beautifulsoup import BeautifulSoup4 as BeautifulSoup |
483 | 7 | >>> from lp.services.feeds.tests.helper import ( | 7 | >>> from lp.services.feeds.tests.helper import ( |
484 | 8 | ... parse_ids, parse_links, validate_feed) | 8 | ... parse_ids, parse_links, validate_feed) |
485 | 9 | 9 | ||
486 | @@ -75,7 +75,7 @@ that have been committed by that person (or attributed to that person). | |||
487 | 75 | ... browser.contents, browser.headers['content-type'], browser.url) | 75 | ... browser.contents, browser.headers['content-type'], browser.url) |
488 | 76 | >>> validate_browser_feed(anon_browser) | 76 | >>> validate_browser_feed(anon_browser) |
489 | 77 | No Errors | 77 | No Errors |
491 | 78 | >>> BSS(anon_browser.contents).title.contents | 78 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
492 | 79 | [u'Latest Revisions by Mike Murphy'] | 79 | [u'Latest Revisions by Mike Murphy'] |
493 | 80 | >>> def print_parse_ids(browser): | 80 | >>> def print_parse_ids(browser): |
494 | 81 | ... for id in parse_ids(browser.contents): | 81 | ... for id in parse_ids(browser.contents): |
495 | @@ -96,7 +96,7 @@ Ensure the self link is correct and there is only one. | |||
496 | 96 | ... for link in parse_links(browser.contents, rel="self"): | 96 | ... for link in parse_links(browser.contents, rel="self"): |
497 | 97 | ... print(link) | 97 | ... print(link) |
498 | 98 | >>> print_parse_links(anon_browser) | 98 | >>> print_parse_links(anon_browser) |
500 | 99 | <link rel="self" href="http://feeds.launchpad.test/~mike/revisions.atom" /> | 99 | <link href="http://feeds.launchpad.test/~mike/revisions.atom" rel="self"/> |
501 | 100 | 100 | ||
502 | 101 | If we look at the feed for a team, we get revisions created by any member | 101 | If we look at the feed for a team, we get revisions created by any member |
503 | 102 | of that team. | 102 | of that team. |
504 | @@ -104,7 +104,7 @@ of that team. | |||
505 | 104 | >>> browser.open('http://feeds.launchpad.test/~m-team/revisions.atom') | 104 | >>> browser.open('http://feeds.launchpad.test/~m-team/revisions.atom') |
506 | 105 | >>> validate_browser_feed(browser) | 105 | >>> validate_browser_feed(browser) |
507 | 106 | No Errors | 106 | No Errors |
509 | 107 | >>> BSS(browser.contents).title.contents | 107 | >>> BeautifulSoup(browser.contents, 'xml').title.contents |
510 | 108 | [u'Latest Revisions by members of The M Team'] | 108 | [u'Latest Revisions by members of The M Team'] |
511 | 109 | >>> print_parse_ids(browser) | 109 | >>> print_parse_ids(browser) |
512 | 110 | <id>tag:launchpad.net,...:/code/~m-team</id> | 110 | <id>tag:launchpad.net,...:/code/~m-team</id> |
513 | @@ -122,7 +122,7 @@ that have been committed on branches for the product. | |||
514 | 122 | >>> anon_browser.open('http://feeds.launchpad.test/fooix/revisions.atom') | 122 | >>> anon_browser.open('http://feeds.launchpad.test/fooix/revisions.atom') |
515 | 123 | >>> validate_browser_feed(anon_browser) | 123 | >>> validate_browser_feed(anon_browser) |
516 | 124 | No Errors | 124 | No Errors |
518 | 125 | >>> BSS(anon_browser.contents).title.contents | 125 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
519 | 126 | [u'Latest Revisions for Fooix'] | 126 | [u'Latest Revisions for Fooix'] |
520 | 127 | 127 | ||
521 | 128 | Ignore the date associated with the id of 'fooix' as this is the date created | 128 | Ignore the date associated with the id of 'fooix' as this is the date created |
522 | @@ -136,7 +136,7 @@ for the product, which will be different each time the test is run. | |||
523 | 136 | Ensure the self link points to the feed location and there is only one. | 136 | Ensure the self link points to the feed location and there is only one. |
524 | 137 | 137 | ||
525 | 138 | >>> print_parse_links(anon_browser) | 138 | >>> print_parse_links(anon_browser) |
527 | 139 | <link rel="self" href="http://feeds.launchpad.test/fooix/revisions.atom" /> | 139 | <link href="http://feeds.launchpad.test/fooix/revisions.atom" rel="self"/> |
528 | 140 | 140 | ||
529 | 141 | 141 | ||
530 | 142 | == Feed for a project group's revisions == | 142 | == Feed for a project group's revisions == |
531 | @@ -147,7 +147,7 @@ branch for any product that is associated with the project group. | |||
532 | 147 | >>> anon_browser.open('http://feeds.launchpad.test/fubar/revisions.atom') | 147 | >>> anon_browser.open('http://feeds.launchpad.test/fubar/revisions.atom') |
533 | 148 | >>> validate_browser_feed(anon_browser) | 148 | >>> validate_browser_feed(anon_browser) |
534 | 149 | No Errors | 149 | No Errors |
536 | 150 | >>> BSS(anon_browser.contents).title.contents | 150 | >>> BeautifulSoup(anon_browser.contents, 'xml').title.contents |
537 | 151 | [u'Latest Revisions for Fubar'] | 151 | [u'Latest Revisions for Fubar'] |
538 | 152 | 152 | ||
539 | 153 | Ignore the date associated with the id of 'fubar' as this is the date created | 153 | Ignore the date associated with the id of 'fubar' as this is the date created |
540 | @@ -163,4 +163,4 @@ of the project group, which will be different each time the test is run. | |||
541 | 163 | Ensure the self link points to the feed location and there is only one. | 163 | Ensure the self link points to the feed location and there is only one. |
542 | 164 | 164 | ||
543 | 165 | >>> print_parse_links(anon_browser) | 165 | >>> print_parse_links(anon_browser) |
545 | 166 | <link rel="self" href="http://feeds.launchpad.test/fubar/revisions.atom" /> | 166 | <link href="http://feeds.launchpad.test/fubar/revisions.atom" rel="self"/> |
546 | diff --git a/lib/lp/registry/stories/announcements/xx-announcements.txt b/lib/lp/registry/stories/announcements/xx-announcements.txt | |||
547 | index f8e1045..addfc4a 100644 | |||
548 | --- a/lib/lp/registry/stories/announcements/xx-announcements.txt | |||
549 | +++ b/lib/lp/registry/stories/announcements/xx-announcements.txt | |||
550 | @@ -7,8 +7,8 @@ dedicated batched page showing all announcements, and as an RSS/Atom | |||
551 | 7 | news feed. | 7 | news feed. |
552 | 8 | 8 | ||
553 | 9 | >>> from lp.services.beautifulsoup import ( | 9 | >>> from lp.services.beautifulsoup import ( |
556 | 10 | ... BeautifulSoup, | 10 | ... BeautifulSoup4 as BeautifulSoup, |
557 | 11 | ... SoupStrainer, | 11 | ... SoupStrainer4 as SoupStrainer, |
558 | 12 | ... ) | 12 | ... ) |
559 | 13 | >>> from lp.services.feeds.tests.helper import ( | 13 | >>> from lp.services.feeds.tests.helper import ( |
560 | 14 | ... parse_ids, parse_links, validate_feed) | 14 | ... parse_ids, parse_links, validate_feed) |
561 | @@ -643,7 +643,7 @@ domain. | |||
562 | 643 | >>> links = parse_links(nopriv_browser.contents, rel='self') | 643 | >>> links = parse_links(nopriv_browser.contents, rel='self') |
563 | 644 | >>> for link in links: | 644 | >>> for link in links: |
564 | 645 | ... print link | 645 | ... print link |
566 | 646 | <link rel="self" href="http://feeds.launchpad.test/netapplet/announcements.atom" /> | 646 | <link href="http://feeds.launchpad.test/netapplet/announcements.atom" rel="self"/> |
567 | 647 | 647 | ||
568 | 648 | >>> for id_ in parse_ids(nopriv_browser.contents): | 648 | >>> for id_ in parse_ids(nopriv_browser.contents): |
569 | 649 | ... print extract_text(id_) | 649 | ... print extract_text(id_) |
570 | @@ -716,7 +716,7 @@ products. | |||
571 | 716 | >>> links = parse_links(nopriv_browser.contents, rel='self') | 716 | >>> links = parse_links(nopriv_browser.contents, rel='self') |
572 | 717 | >>> for link in links: | 717 | >>> for link in links: |
573 | 718 | ... print link | 718 | ... print link |
575 | 719 | <link rel="self" href="http://feeds.launchpad.test/apache/announcements.atom" /> | 719 | <link href="http://feeds.launchpad.test/apache/announcements.atom" rel="self"/> |
576 | 720 | 720 | ||
577 | 721 | Finally, there is a feed for all announcements across all projects | 721 | Finally, there is a feed for all announcements across all projects |
578 | 722 | hosted in Launchpad: | 722 | hosted in Launchpad: |
579 | @@ -755,18 +755,16 @@ let us use a DTD to define the html entities that standard xml is missing. | |||
580 | 755 | No Errors | 755 | No Errors |
581 | 756 | >>> soup = BeautifulSoup(nopriv_browser.contents) | 756 | >>> soup = BeautifulSoup(nopriv_browser.contents) |
582 | 757 | >>> soup.find('feed').entry.title | 757 | >>> soup.find('feed').entry.title |
586 | 758 | <...>Ampersand="&" LessThan="<" | 758 | <...>Ampersand="&" LessThan="<" GreaterThan=">"</title> |
587 | 759 | GreaterThan=">"</title> | 759 | >>> print(soup.find('feed').entry.content) |
585 | 760 | >>> soup.find('feed').entry.content | ||
588 | 761 | <... | 760 | <... |
597 | 762 | Ampersand=&quot;&amp;&quot;<br /> | 761 | Ampersand="&amp;"<br/> |
598 | 763 | LessThan=&quot;&lt;&quot;<br /> | 762 | LessThan="&lt;"<br/> |
599 | 764 | GreaterThan=&quot;&gt;&quot;<br /> | 763 | GreaterThan="&gt;"<br/> |
600 | 765 | Newline=&quot;<br /> | 764 | Newline="<br/> |
601 | 766 | &quot;<br /> | 765 | "<br/> |
602 | 767 | url=&quot;<a rel="nofollow" | 766 | url="<a href="http://www.ubuntu.com" |
603 | 768 | href="http://www.ubuntu.com">http://<wbr | 767 | rel="nofollow">http://<wbr/>www.ubuntu.<wbr/>com</a>"... |
596 | 769 | />www.ubuntu.<wbr />com</a>&quot;... | ||
604 | 770 | 768 | ||
605 | 771 | 769 | ||
606 | 772 | Deletion | 770 | Deletion |
607 | diff --git a/lib/lp/services/feeds/doc/feeds.txt b/lib/lp/services/feeds/doc/feeds.txt | |||
608 | index 35086d2..02315cb 100644 | |||
609 | --- a/lib/lp/services/feeds/doc/feeds.txt | |||
610 | +++ b/lib/lp/services/feeds/doc/feeds.txt | |||
611 | @@ -157,7 +157,7 @@ we are testing xhtml encoding here in case we need it in the future. | |||
612 | 157 | >>> xhtml = FeedTypedData("<b> and and &</b><hr/>", | 157 | >>> xhtml = FeedTypedData("<b> and and &</b><hr/>", |
613 | 158 | ... content_type="xhtml") | 158 | ... content_type="xhtml") |
614 | 159 | >>> xhtml.content | 159 | >>> xhtml.content |
616 | 160 | u'<b> and \xa0 and &</b><hr />' | 160 | u'<b> and \xa0 and &</b><hr/>' |
617 | 161 | 161 | ||
618 | 162 | 162 | ||
619 | 163 | == validate_feed() helper function == | 163 | == validate_feed() helper function == |
620 | diff --git a/lib/lp/services/feeds/feed.py b/lib/lp/services/feeds/feed.py | |||
621 | index 9462062..143121c 100644 | |||
622 | --- a/lib/lp/services/feeds/feed.py | |||
623 | +++ b/lib/lp/services/feeds/feed.py | |||
624 | @@ -27,7 +27,7 @@ from zope.component import getUtility | |||
625 | 27 | from zope.datetime import rfc1123_date | 27 | from zope.datetime import rfc1123_date |
626 | 28 | from zope.interface import implementer | 28 | from zope.interface import implementer |
627 | 29 | 29 | ||
629 | 30 | from lp.services.beautifulsoup import BeautifulSoup | 30 | from lp.services.beautifulsoup import BeautifulSoup4 as BeautifulSoup |
630 | 31 | from lp.services.config import config | 31 | from lp.services.config import config |
631 | 32 | from lp.services.feeds.interfaces.feed import ( | 32 | from lp.services.feeds.interfaces.feed import ( |
632 | 33 | IFeed, | 33 | IFeed, |
633 | @@ -302,9 +302,7 @@ class FeedTypedData: | |||
634 | 302 | if self.content_type in ('text', 'html'): | 302 | if self.content_type in ('text', 'html'): |
635 | 303 | altered_content = html_escape(altered_content) | 303 | altered_content = html_escape(altered_content) |
636 | 304 | elif self.content_type == 'xhtml': | 304 | elif self.content_type == 'xhtml': |
640 | 305 | soup = BeautifulSoup( | 305 | soup = BeautifulSoup(altered_content) |
638 | 306 | altered_content, | ||
639 | 307 | convertEntities=BeautifulSoup.HTML_ENTITIES) | ||
641 | 308 | altered_content = unicode(soup) | 306 | altered_content = unicode(soup) |
642 | 309 | return altered_content | 307 | return altered_content |
643 | 310 | 308 | ||
644 | diff --git a/lib/lp/services/feeds/stories/xx-links.txt b/lib/lp/services/feeds/stories/xx-links.txt | |||
645 | index 83110fd..23e467b 100644 | |||
646 | --- a/lib/lp/services/feeds/stories/xx-links.txt | |||
647 | +++ b/lib/lp/services/feeds/stories/xx-links.txt | |||
648 | @@ -11,13 +11,13 @@ launchpad.test to provide links to corresponding Atom feeds. | |||
649 | 11 | The root launchpad.test url will have a link to the Atom feed which | 11 | The root launchpad.test url will have a link to the Atom feed which |
650 | 12 | displays the most recent announcements for all the projects. | 12 | displays the most recent announcements for all the projects. |
651 | 13 | 13 | ||
653 | 14 | >>> from lp.services.beautifulsoup import BeautifulSoup | 14 | >>> from lp.services.beautifulsoup import BeautifulSoup4 as BeautifulSoup |
654 | 15 | >>> browser.open('http://launchpad.test/') | 15 | >>> browser.open('http://launchpad.test/') |
655 | 16 | >>> soup = BeautifulSoup(browser.contents) | 16 | >>> soup = BeautifulSoup(browser.contents) |
656 | 17 | >>> soup.head.findAll('link', type='application/atom+xml') | 17 | >>> soup.head.findAll('link', type='application/atom+xml') |
660 | 18 | [<link rel="alternate" type="application/atom+xml" | 18 | [<link href="http://feeds.launchpad.test/announcements.atom" |
661 | 19 | href="http://feeds.launchpad.test/announcements.atom" | 19 | rel="alternate" title="All Announcements" |
662 | 20 | title="All Announcements" />] | 20 | type="application/atom+xml"/>] |
663 | 21 | 21 | ||
664 | 22 | The http://launchpad.test/+announcements page also displays recent | 22 | The http://launchpad.test/+announcements page also displays recent |
665 | 23 | announcements for all the projects so it should have a link to the same | 23 | announcements for all the projects so it should have a link to the same |
666 | @@ -26,9 +26,9 @@ feed. | |||
667 | 26 | >>> browser.open('http://launchpad.test/+announcements') | 26 | >>> browser.open('http://launchpad.test/+announcements') |
668 | 27 | >>> soup = BeautifulSoup(browser.contents) | 27 | >>> soup = BeautifulSoup(browser.contents) |
669 | 28 | >>> soup.head.findAll('link', type='application/atom+xml') | 28 | >>> soup.head.findAll('link', type='application/atom+xml') |
673 | 29 | [<link rel="alternate" type="application/atom+xml" | 29 | [<link href="http://feeds.launchpad.test/announcements.atom" |
674 | 30 | href="http://feeds.launchpad.test/announcements.atom" | 30 | rel="alternate" title="All Announcements" |
675 | 31 | title="All Announcements" />] | 31 | type="application/atom+xml"/>] |
676 | 32 | 32 | ||
677 | 33 | == Single Bug Feed == | 33 | == Single Bug Feed == |
678 | 34 | 34 | ||
679 | @@ -38,9 +38,9 @@ atom feed for that one bug. | |||
680 | 38 | >>> browser.open('http://bugs.launchpad.test/firefox/+bug/1') | 38 | >>> browser.open('http://bugs.launchpad.test/firefox/+bug/1') |
681 | 39 | >>> soup = BeautifulSoup(browser.contents) | 39 | >>> soup = BeautifulSoup(browser.contents) |
682 | 40 | >>> soup.head.findAll('link', type='application/atom+xml') | 40 | >>> soup.head.findAll('link', type='application/atom+xml') |
686 | 41 | [<link rel="alternate" type="application/atom+xml" | 41 | [<link href="http://feeds.launchpad.test/bugs/1/bug.atom" |
687 | 42 | href="http://feeds.launchpad.test/bugs/1/bug.atom" | 42 | rel="alternate" title="Bug 1 Feed" |
688 | 43 | title="Bug 1 Feed" />] | 43 | type="application/atom+xml"/>] |
689 | 44 | 44 | ||
690 | 45 | But if the bug is private, there should be no link. | 45 | But if the bug is private, there should be no link. |
691 | 46 | 46 | ||
692 | @@ -80,15 +80,15 @@ branches. | |||
693 | 80 | >>> browser.open('http://launchpad.test/~stevea') | 80 | >>> browser.open('http://launchpad.test/~stevea') |
694 | 81 | >>> soup = BeautifulSoup(browser.contents) | 81 | >>> soup = BeautifulSoup(browser.contents) |
695 | 82 | >>> soup.head.findAll('link', type='application/atom+xml') | 82 | >>> soup.head.findAll('link', type='application/atom+xml') |
705 | 83 | [<link rel="alternate" type="application/atom+xml" | 83 | [<link href="http://feeds.launchpad.test/~stevea/latest-bugs.atom" |
706 | 84 | href="http://feeds.launchpad.test/~stevea/latest-bugs.atom" | 84 | rel="alternate" title="Latest Bugs for Steve Alexander" |
707 | 85 | title="Latest Bugs for Steve Alexander" />, | 85 | type="application/atom+xml"/>, |
708 | 86 | <link rel="alternate" type="application/atom+xml" | 86 | <link href="http://feeds.launchpad.test/~stevea/branches.atom" |
709 | 87 | href="http://feeds.launchpad.test/~stevea/branches.atom" | 87 | rel="alternate" title="Latest Branches for Steve Alexander" |
710 | 88 | title="Latest Branches for Steve Alexander" />, | 88 | type="application/atom+xml"/>, |
711 | 89 | <link rel="alternate" type="application/atom+xml" | 89 | <link href="http://feeds.launchpad.test/~stevea/revisions.atom" |
712 | 90 | href="http://feeds.launchpad.test/~stevea/revisions.atom" | 90 | rel="alternate" title="Latest Revisions by Steve Alexander" |
713 | 91 | title="Latest Revisions by Steve Alexander" />] | 91 | type="application/atom+xml"/>] |
714 | 92 | 92 | ||
715 | 93 | On the bugs subdomain, only a link to the bugs feed will be included, | 93 | On the bugs subdomain, only a link to the bugs feed will be included, |
716 | 94 | not the branches link. | 94 | not the branches link. |
717 | @@ -96,9 +96,9 @@ not the branches link. | |||
718 | 96 | >>> browser.open('http://bugs.launchpad.test/~stevea') | 96 | >>> browser.open('http://bugs.launchpad.test/~stevea') |
719 | 97 | >>> soup = BeautifulSoup(browser.contents) | 97 | >>> soup = BeautifulSoup(browser.contents) |
720 | 98 | >>> soup.head.findAll('link', type='application/atom+xml') | 98 | >>> soup.head.findAll('link', type='application/atom+xml') |
724 | 99 | [<link rel="alternate" type="application/atom+xml" | 99 | [<link href="http://feeds.launchpad.test/~stevea/latest-bugs.atom" |
725 | 100 | href="http://feeds.launchpad.test/~stevea/latest-bugs.atom" | 100 | rel="alternate" title="Latest Bugs for Steve Alexander" |
726 | 101 | title="Latest Bugs for Steve Alexander" />] | 101 | type="application/atom+xml"/>] |
727 | 102 | 102 | ||
728 | 103 | 103 | ||
729 | 104 | == Latest Bugs, Branches, and Announcements for a Product == | 104 | == Latest Bugs, Branches, and Announcements for a Product == |
730 | @@ -112,27 +112,27 @@ main product page. | |||
731 | 112 | >>> browser.open('http://launchpad.test/jokosher') | 112 | >>> browser.open('http://launchpad.test/jokosher') |
732 | 113 | >>> soup = BeautifulSoup(browser.contents) | 113 | >>> soup = BeautifulSoup(browser.contents) |
733 | 114 | >>> soup.head.findAll('link', type='application/atom+xml') | 114 | >>> soup.head.findAll('link', type='application/atom+xml') |
746 | 115 | [<link rel="alternate" type="application/atom+xml" | 115 | [<link href="http://feeds.launchpad.test/jokosher/announcements.atom" |
747 | 116 | href="http://feeds.launchpad.test/jokosher/announcements.atom" | 116 | rel="alternate" title="Announcements for Jokosher" |
748 | 117 | title="Announcements for Jokosher" />, | 117 | type="application/atom+xml"/>, |
749 | 118 | <link rel="alternate" type="application/atom+xml" | 118 | <link href="http://feeds.launchpad.test/jokosher/latest-bugs.atom" |
750 | 119 | href="http://feeds.launchpad.test/jokosher/latest-bugs.atom" | 119 | rel="alternate" title="Latest Bugs for Jokosher" |
751 | 120 | title="Latest Bugs for Jokosher" />, | 120 | type="application/atom+xml"/>, |
752 | 121 | <link rel="alternate" type="application/atom+xml" | 121 | <link href="http://feeds.launchpad.test/jokosher/branches.atom" |
753 | 122 | href="http://feeds.launchpad.test/jokosher/branches.atom" | 122 | rel="alternate" title="Latest Branches for Jokosher" |
754 | 123 | title="Latest Branches for Jokosher" />, | 123 | type="application/atom+xml"/>, |
755 | 124 | <link rel="alternate" type="application/atom+xml" | 124 | <link href="http://feeds.launchpad.test/jokosher/revisions.atom" |
756 | 125 | href="http://feeds.launchpad.test/jokosher/revisions.atom" | 125 | rel="alternate" title="Latest Revisions for Jokosher" |
757 | 126 | title="Latest Revisions for Jokosher" />] | 126 | type="application/atom+xml"/>] |
758 | 127 | 127 | ||
759 | 128 | Only bug feeds should be linked to on bugs.launchpad.test. | 128 | Only bug feeds should be linked to on bugs.launchpad.test. |
760 | 129 | 129 | ||
761 | 130 | >>> browser.open('http://bugs.launchpad.test/jokosher') | 130 | >>> browser.open('http://bugs.launchpad.test/jokosher') |
762 | 131 | >>> soup = BeautifulSoup(browser.contents) | 131 | >>> soup = BeautifulSoup(browser.contents) |
763 | 132 | >>> soup.head.findAll('link', type='application/atom+xml') | 132 | >>> soup.head.findAll('link', type='application/atom+xml') |
767 | 133 | [<link rel="alternate" type="application/atom+xml" | 133 | [<link href="http://feeds.launchpad.test/jokosher/latest-bugs.atom" |
768 | 134 | href="http://feeds.launchpad.test/jokosher/latest-bugs.atom" | 134 | rel="alternate" title="Latest Bugs for Jokosher" |
769 | 135 | title="Latest Bugs for Jokosher" />] | 135 | type="application/atom+xml"/>] |
770 | 136 | 136 | ||
771 | 137 | 137 | ||
772 | 138 | == Escaping the title == | 138 | == Escaping the title == |
773 | @@ -160,18 +160,22 @@ it must have quotes and html escaped. | |||
774 | 160 | >>> browser.open('http://launchpad.test/bad-displayname') | 160 | >>> browser.open('http://launchpad.test/bad-displayname') |
775 | 161 | >>> soup = BeautifulSoup(browser.contents) | 161 | >>> soup = BeautifulSoup(browser.contents) |
776 | 162 | >>> soup.head.findAll('link', type='application/atom+xml') | 162 | >>> soup.head.findAll('link', type='application/atom+xml') |
789 | 163 | [<link rel="alternate" type="application/atom+xml" | 163 | [<link href="http://feeds.launchpad.test/bad-displayname/announcements.atom" |
790 | 164 | href="http://feeds.launchpad.test/bad-displayname/announcements.atom" | 164 | rel="alternate" |
791 | 165 | title='Announcements for Bad displayname"><script>alert("h4x0r")</script>' />, | 165 | title='Announcements for Bad displayname"><script>alert("h4x0r")</script>' |
792 | 166 | <link rel="alternate" type="application/atom+xml" | 166 | type="application/atom+xml"/>, |
793 | 167 | href="http://feeds.launchpad.test/bad-displayname/latest-bugs.atom" | 167 | <link href="http://feeds.launchpad.test/bad-displayname/latest-bugs.atom" |
794 | 168 | title='Latest Bugs for Bad displayname"><script>alert("h4x0r")</script>' />, | 168 | rel="alternate" |
795 | 169 | <link rel="alternate" type="application/atom+xml" | 169 | title='Latest Bugs for Bad displayname"><script>alert("h4x0r")</script>' |
796 | 170 | href="http://feeds.launchpad.test/bad-displayname/branches.atom" | 170 | type="application/atom+xml"/>, |
797 | 171 | title='Latest Branches for Bad displayname"><script>alert("h4x0r")</script>' />, | 171 | <link href="http://feeds.launchpad.test/bad-displayname/branches.atom" |
798 | 172 | <link rel="alternate" type="application/atom+xml" | 172 | rel="alternate" |
799 | 173 | href="http://feeds.launchpad.test/bad-displayname/revisions.atom" | 173 | title='Latest Branches for Bad displayname"><script>alert("h4x0r")</script>' |
800 | 174 | title='Latest Revisions for Bad displayname"><script>alert("h4x0r")</script>' />] | 174 | type="application/atom+xml"/>, |
801 | 175 | <link href="http://feeds.launchpad.test/bad-displayname/revisions.atom" | ||
802 | 176 | rel="alternate" | ||
803 | 177 | title='Latest Revisions for Bad displayname"><script>alert("h4x0r")</script>' | ||
804 | 178 | type="application/atom+xml"/>] | ||
805 | 175 | 179 | ||
806 | 176 | == Latest Bugs for a ProjectGroup == | 180 | == Latest Bugs for a ProjectGroup == |
807 | 177 | 181 | ||
808 | @@ -184,27 +188,27 @@ on the main project group page. | |||
809 | 184 | >>> browser.open('http://launchpad.test/gnome') | 188 | >>> browser.open('http://launchpad.test/gnome') |
810 | 185 | >>> soup = BeautifulSoup(browser.contents) | 189 | >>> soup = BeautifulSoup(browser.contents) |
811 | 186 | >>> soup.head.findAll('link', type='application/atom+xml') | 190 | >>> soup.head.findAll('link', type='application/atom+xml') |
824 | 187 | [<link rel="alternate" type="application/atom+xml" | 191 | [<link href="http://feeds.launchpad.test/gnome/announcements.atom" |
825 | 188 | href="http://feeds.launchpad.test/gnome/announcements.atom" | 192 | rel="alternate" title="Announcements for GNOME" |
826 | 189 | title="Announcements for GNOME" />, | 193 | type="application/atom+xml"/>, |
827 | 190 | <link rel="alternate" type="application/atom+xml" | 194 | <link href="http://feeds.launchpad.test/gnome/latest-bugs.atom" |
828 | 191 | href="http://feeds.launchpad.test/gnome/latest-bugs.atom" | 195 | rel="alternate" title="Latest Bugs for GNOME" |
829 | 192 | title="Latest Bugs for GNOME" />, | 196 | type="application/atom+xml"/>, |
830 | 193 | <link rel="alternate" type="application/atom+xml" | 197 | <link href="http://feeds.launchpad.test/gnome/branches.atom" |
831 | 194 | href="http://feeds.launchpad.test/gnome/branches.atom" | 198 | rel="alternate" title="Latest Branches for GNOME" |
832 | 195 | title="Latest Branches for GNOME" />, | 199 | type="application/atom+xml"/>, |
833 | 196 | <link rel="alternate" type="application/atom+xml" | 200 | <link href="http://feeds.launchpad.test/gnome/revisions.atom" |
834 | 197 | href="http://feeds.launchpad.test/gnome/revisions.atom" | 201 | rel="alternate" title="Latest Revisions for GNOME" |
835 | 198 | title="Latest Revisions for GNOME" />] | 202 | type="application/atom+xml"/>] |
836 | 199 | 203 | ||
837 | 200 | Only bug feeds should be linked to on bugs.launchpad.test. | 204 | Only bug feeds should be linked to on bugs.launchpad.test. |
838 | 201 | 205 | ||
839 | 202 | >>> browser.open('http://bugs.launchpad.test/gnome') | 206 | >>> browser.open('http://bugs.launchpad.test/gnome') |
840 | 203 | >>> soup = BeautifulSoup(browser.contents) | 207 | >>> soup = BeautifulSoup(browser.contents) |
841 | 204 | >>> soup.head.findAll('link', type='application/atom+xml') | 208 | >>> soup.head.findAll('link', type='application/atom+xml') |
845 | 205 | [<link rel="alternate" type="application/atom+xml" | 209 | [<link href="http://feeds.launchpad.test/gnome/latest-bugs.atom" |
846 | 206 | href="http://feeds.launchpad.test/gnome/latest-bugs.atom" | 210 | rel="alternate" title="Latest Bugs for GNOME" |
847 | 207 | title="Latest Bugs for GNOME" />] | 211 | type="application/atom+xml"/>] |
848 | 208 | 212 | ||
849 | 209 | The default view for a project group on bugs.launchpad.test is +bugs. The | 213 | The default view for a project group on bugs.launchpad.test is +bugs. The |
850 | 210 | default bug listing matches the latest-bugs atom feed, but any search | 214 | default bug listing matches the latest-bugs atom feed, but any search |
851 | @@ -231,21 +235,21 @@ An announcements feed link should also be shown on the main distro page. | |||
852 | 231 | >>> browser.open('http://launchpad.test/ubuntu') | 235 | >>> browser.open('http://launchpad.test/ubuntu') |
853 | 232 | >>> soup = BeautifulSoup(browser.contents) | 236 | >>> soup = BeautifulSoup(browser.contents) |
854 | 233 | >>> soup.head.findAll('link', type='application/atom+xml') | 237 | >>> soup.head.findAll('link', type='application/atom+xml') |
861 | 234 | [<link rel="alternate" type="application/atom+xml" | 238 | [<link href="http://feeds.launchpad.test/ubuntu/announcements.atom" |
862 | 235 | href="http://feeds.launchpad.test/ubuntu/announcements.atom" | 239 | rel="alternate" title="Announcements for Ubuntu" |
863 | 236 | title="Announcements for Ubuntu" />, | 240 | type="application/atom+xml"/>, |
864 | 237 | <link rel="alternate" type="application/atom+xml" | 241 | <link href="http://feeds.launchpad.test/ubuntu/latest-bugs.atom" |
865 | 238 | href="http://feeds.launchpad.test/ubuntu/latest-bugs.atom" | 242 | rel="alternate" title="Latest Bugs for Ubuntu" |
866 | 239 | title="Latest Bugs for Ubuntu" />] | 243 | type="application/atom+xml"/>] |
867 | 240 | 244 | ||
868 | 241 | Only bug feeds should be linked to on bugs.launchpad.test. | 245 | Only bug feeds should be linked to on bugs.launchpad.test. |
869 | 242 | 246 | ||
870 | 243 | >>> browser.open('http://bugs.launchpad.test/ubuntu') | 247 | >>> browser.open('http://bugs.launchpad.test/ubuntu') |
871 | 244 | >>> soup = BeautifulSoup(browser.contents) | 248 | >>> soup = BeautifulSoup(browser.contents) |
872 | 245 | >>> soup.head.findAll('link', type='application/atom+xml') | 249 | >>> soup.head.findAll('link', type='application/atom+xml') |
876 | 246 | [<link rel="alternate" type="application/atom+xml" | 250 | [<link href="http://feeds.launchpad.test/ubuntu/latest-bugs.atom" |
877 | 247 | href="http://feeds.launchpad.test/ubuntu/latest-bugs.atom" | 251 | rel="alternate" title="Latest Bugs for Ubuntu" |
878 | 248 | title="Latest Bugs for Ubuntu" />] | 252 | type="application/atom+xml"/>] |
879 | 249 | 253 | ||
880 | 250 | 254 | ||
881 | 251 | == Latest Bugs for a Distroseries == | 255 | == Latest Bugs for a Distroseries == |
882 | @@ -256,9 +260,10 @@ show a link to the atom feed for that distroseries' latest bugs. | |||
883 | 256 | >>> browser.open('http://bugs.launchpad.test/ubuntu/hoary') | 260 | >>> browser.open('http://bugs.launchpad.test/ubuntu/hoary') |
884 | 257 | >>> soup = BeautifulSoup(browser.contents) | 261 | >>> soup = BeautifulSoup(browser.contents) |
885 | 258 | >>> soup.head.findAll('link', type='application/atom+xml') | 262 | >>> soup.head.findAll('link', type='application/atom+xml') |
887 | 259 | [<link rel="alternate" type="application/atom+xml" | 263 | [<link |
888 | 260 | href="http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom" | 264 | href="http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom" |
890 | 261 | title="Latest Bugs for Hoary" />] | 265 | rel="alternate" title="Latest Bugs for Hoary" |
891 | 266 | type="application/atom+xml"/>] | ||
892 | 262 | 267 | ||
893 | 263 | 268 | ||
894 | 264 | == Latest Bugs for a Product Series == | 269 | == Latest Bugs for a Product Series == |
895 | @@ -269,9 +274,9 @@ show a link to the atom feed for that product series' latest bugs. | |||
896 | 269 | >>> browser.open('http://bugs.launchpad.test/firefox/1.0') | 274 | >>> browser.open('http://bugs.launchpad.test/firefox/1.0') |
897 | 270 | >>> soup = BeautifulSoup(browser.contents) | 275 | >>> soup = BeautifulSoup(browser.contents) |
898 | 271 | >>> soup.head.findAll('link', type='application/atom+xml') | 276 | >>> soup.head.findAll('link', type='application/atom+xml') |
902 | 272 | [<link rel="alternate" type="application/atom+xml" | 277 | [<link href="http://feeds.launchpad.test/firefox/1.0/latest-bugs.atom" |
903 | 273 | href="http://feeds.launchpad.test/firefox/1.0/latest-bugs.atom" | 278 | rel="alternate" title="Latest Bugs for 1.0" |
904 | 274 | title="Latest Bugs for 1.0" />] | 279 | type="application/atom+xml"/>] |
905 | 275 | 280 | ||
906 | 276 | 281 | ||
907 | 277 | == Latest Bugs for a Source Package == | 282 | == Latest Bugs for a Source Package == |
908 | @@ -282,9 +287,10 @@ show a link to the atom feed for that source package's latest bugs. | |||
909 | 282 | >>> browser.open('http://bugs.launchpad.test/ubuntu/+source/cnews') | 287 | >>> browser.open('http://bugs.launchpad.test/ubuntu/+source/cnews') |
910 | 283 | >>> soup = BeautifulSoup(browser.contents) | 288 | >>> soup = BeautifulSoup(browser.contents) |
911 | 284 | >>> soup.head.findAll('link', type='application/atom+xml') | 289 | >>> soup.head.findAll('link', type='application/atom+xml') |
913 | 285 | [<link rel="alternate" type="application/atom+xml" | 290 | [<link |
914 | 286 | href="http://feeds.launchpad.test/ubuntu/+source/cnews/latest-bugs.atom" | 291 | href="http://feeds.launchpad.test/ubuntu/+source/cnews/latest-bugs.atom" |
916 | 287 | title="Latest Bugs for cnews in Ubuntu" />] | 292 | rel="alternate" title="Latest Bugs for cnews in Ubuntu" |
917 | 293 | type="application/atom+xml"/>] | ||
918 | 288 | 294 | ||
919 | 289 | 295 | ||
920 | 290 | == Latest Branches for a ProjectGroup == | 296 | == Latest Branches for a ProjectGroup == |
921 | @@ -295,12 +301,14 @@ to the atom feed for that project group's latest branches. | |||
922 | 295 | >>> browser.open('http://code.launchpad.test/mozilla') | 301 | >>> browser.open('http://code.launchpad.test/mozilla') |
923 | 296 | >>> soup = BeautifulSoup(browser.contents) | 302 | >>> soup = BeautifulSoup(browser.contents) |
924 | 297 | >>> soup.head.findAll('link', type='application/atom+xml') | 303 | >>> soup.head.findAll('link', type='application/atom+xml') |
926 | 298 | [<link rel="alternate" type="application/atom+xml" | 304 | [<link |
927 | 299 | href="http://feeds.launchpad.test/mozilla/branches.atom" | 305 | href="http://feeds.launchpad.test/mozilla/branches.atom" |
930 | 300 | title="Latest Branches for The Mozilla Project" />, | 306 | rel="alternate" title="Latest Branches for The Mozilla Project" |
931 | 301 | <link rel="alternate" type="application/atom+xml" | 307 | type="application/atom+xml"/>, |
932 | 308 | <link | ||
933 | 302 | href="http://feeds.launchpad.test/mozilla/revisions.atom" | 309 | href="http://feeds.launchpad.test/mozilla/revisions.atom" |
935 | 303 | title="Latest Revisions for The Mozilla Project" />] | 310 | rel="alternate" title="Latest Revisions for The Mozilla Project" |
936 | 311 | type="application/atom+xml"/>] | ||
937 | 304 | 312 | ||
938 | 305 | 313 | ||
939 | 306 | == Latest Branches for a Product == | 314 | == Latest Branches for a Product == |
940 | @@ -311,12 +319,13 @@ to the atom feed for that product's latest branches. | |||
941 | 311 | >>> browser.open('http://code.launchpad.test/firefox') | 319 | >>> browser.open('http://code.launchpad.test/firefox') |
942 | 312 | >>> soup = BeautifulSoup(browser.contents) | 320 | >>> soup = BeautifulSoup(browser.contents) |
943 | 313 | >>> soup.head.findAll('link', type='application/atom+xml') | 321 | >>> soup.head.findAll('link', type='application/atom+xml') |
950 | 314 | [<link rel="alternate" type="application/atom+xml" | 322 | [<link href="http://feeds.launchpad.test/firefox/branches.atom" |
951 | 315 | href="http://feeds.launchpad.test/firefox/branches.atom" | 323 | rel="alternate" title="Latest Branches for Mozilla Firefox" |
952 | 316 | title="Latest Branches for Mozilla Firefox" />, | 324 | type="application/atom+xml"/>, |
953 | 317 | <link rel="alternate" type="application/atom+xml" | 325 | <link href="http://feeds.launchpad.test/firefox/revisions.atom" |
954 | 318 | href="http://feeds.launchpad.test/firefox/revisions.atom" | 326 | rel="alternate" |
955 | 319 | title="Latest Revisions for Mozilla Firefox" />] | 327 | title="Latest Revisions for Mozilla Firefox" |
956 | 328 | type="application/atom+xml"/>] | ||
957 | 320 | 329 | ||
958 | 321 | 330 | ||
959 | 322 | == Latest Branches for a Person == | 331 | == Latest Branches for a Person == |
960 | @@ -327,12 +336,12 @@ to the atom feed for that person's latest branches. | |||
961 | 327 | >>> browser.open('http://code.launchpad.test/~mark') | 336 | >>> browser.open('http://code.launchpad.test/~mark') |
962 | 328 | >>> soup = BeautifulSoup(browser.contents) | 337 | >>> soup = BeautifulSoup(browser.contents) |
963 | 329 | >>> soup.head.findAll('link', type='application/atom+xml') | 338 | >>> soup.head.findAll('link', type='application/atom+xml') |
970 | 330 | [<link rel="alternate" type="application/atom+xml" | 339 | [<link href="http://feeds.launchpad.test/~mark/branches.atom" |
971 | 331 | href="http://feeds.launchpad.test/~mark/branches.atom" | 340 | rel="alternate" title="Latest Branches for Mark Shuttleworth" |
972 | 332 | title="Latest Branches for Mark Shuttleworth" />, | 341 | type="application/atom+xml"/>, |
973 | 333 | <link rel="alternate" type="application/atom+xml" | 342 | <link href="http://feeds.launchpad.test/~mark/revisions.atom" |
974 | 334 | href="http://feeds.launchpad.test/~mark/revisions.atom" | 343 | rel="alternate" title="Latest Revisions by Mark Shuttleworth" |
975 | 335 | title="Latest Revisions by Mark Shuttleworth" />] | 344 | type="application/atom+xml"/>] |
976 | 336 | 345 | ||
977 | 337 | 346 | ||
978 | 338 | == Latest Revisions on a Branch == | 347 | == Latest Revisions on a Branch == |
979 | @@ -344,9 +353,11 @@ atom feed for that branch's revisions. | |||
980 | 344 | >>> browser.open(url) | 353 | >>> browser.open(url) |
981 | 345 | >>> soup = BeautifulSoup(browser.contents) | 354 | >>> soup = BeautifulSoup(browser.contents) |
982 | 346 | >>> soup.head.findAll('link', type='application/atom+xml') | 355 | >>> soup.head.findAll('link', type='application/atom+xml') |
984 | 347 | [<link rel="alternate" type="application/atom+xml" | 356 | [<link |
985 | 348 | href="http://feeds.launchpad.test/~mark/firefox/release--0.9.1/branch.atom" | 357 | href="http://feeds.launchpad.test/~mark/firefox/release--0.9.1/branch.atom" |
987 | 349 | title="Latest Revisions for Branch lp://dev/~mark/firefox/release--0.9.1" />] | 358 | rel="alternate" |
988 | 359 | title="Latest Revisions for Branch lp://dev/~mark/firefox/release--0.9.1" | ||
989 | 360 | type="application/atom+xml"/>] | ||
990 | 350 | 361 | ||
991 | 351 | But if the branch is private, there should be no link. | 362 | But if the branch is private, there should be no link. |
992 | 352 | 363 | ||
993 | diff --git a/lib/lp/services/feeds/stories/xx-security.txt b/lib/lp/services/feeds/stories/xx-security.txt | |||
994 | index ea7122d..2e441df 100644 | |||
995 | --- a/lib/lp/services/feeds/stories/xx-security.txt | |||
996 | +++ b/lib/lp/services/feeds/stories/xx-security.txt | |||
997 | @@ -4,32 +4,32 @@ Feeds do not display private bugs | |||
998 | 4 | Feeds never contain private bugs, as we are serving feeds over HTTP. | 4 | Feeds never contain private bugs, as we are serving feeds over HTTP. |
999 | 5 | First, set all the bugs to private. | 5 | First, set all the bugs to private. |
1000 | 6 | 6 | ||
1001 | 7 | >>> from zope.security.interfaces import Unauthorized | ||
1002 | 8 | >>> from BeautifulSoup import BeautifulStoneSoup as BSS | ||
1003 | 9 | >>> from lp.services.database.interfaces import IStore | ||
1004 | 10 | >>> import transaction | 7 | >>> import transaction |
1006 | 11 | >>> from lp.bugs.model.bug import Bug | 8 | >>> from zope.security.interfaces import Unauthorized |
1007 | 12 | >>> from lp.app.enums import InformationType | 9 | >>> from lp.app.enums import InformationType |
1008 | 10 | >>> from lp.bugs.model.bug import Bug | ||
1009 | 11 | >>> from lp.services.beautifulsoup import BeautifulSoup4 as BeautifulSoup | ||
1010 | 12 | >>> from lp.services.database.interfaces import IStore | ||
1011 | 13 | >>> IStore(Bug).find(Bug).set(information_type=InformationType.USERDATA) | 13 | >>> IStore(Bug).find(Bug).set(information_type=InformationType.USERDATA) |
1012 | 14 | >>> transaction.commit() | 14 | >>> transaction.commit() |
1013 | 15 | 15 | ||
1014 | 16 | There should be zero entries in these feeds, since all the bugs are private. | 16 | There should be zero entries in these feeds, since all the bugs are private. |
1015 | 17 | 17 | ||
1016 | 18 | >>> browser.open('http://feeds.launchpad.test/jokosher/latest-bugs.atom') | 18 | >>> browser.open('http://feeds.launchpad.test/jokosher/latest-bugs.atom') |
1018 | 19 | >>> BSS(browser.contents)('entry') | 19 | >>> BeautifulSoup(browser.contents, 'xml')('entry') |
1019 | 20 | [] | 20 | [] |
1020 | 21 | 21 | ||
1021 | 22 | >>> browser.open('http://feeds.launchpad.test/mozilla/latest-bugs.atom') | 22 | >>> browser.open('http://feeds.launchpad.test/mozilla/latest-bugs.atom') |
1023 | 23 | >>> BSS(browser.contents)('entry') | 23 | >>> BeautifulSoup(browser.contents, 'xml')('entry') |
1024 | 24 | [] | 24 | [] |
1025 | 25 | 25 | ||
1026 | 26 | >>> browser.open('http://feeds.launchpad.test/~name16/latest-bugs.atom') | 26 | >>> browser.open('http://feeds.launchpad.test/~name16/latest-bugs.atom') |
1028 | 27 | >>> BSS(browser.contents)('entry') | 27 | >>> BeautifulSoup(browser.contents, 'xml')('entry') |
1029 | 28 | [] | 28 | [] |
1030 | 29 | 29 | ||
1031 | 30 | >>> browser.open( | 30 | >>> browser.open( |
1032 | 31 | ... 'http://feeds.launchpad.test/~simple-team/latest-bugs.atom') | 31 | ... 'http://feeds.launchpad.test/~simple-team/latest-bugs.atom') |
1034 | 32 | >>> BSS(browser.contents)('entry') | 32 | >>> BeautifulSoup(browser.contents, 'xml')('entry') |
1035 | 33 | [] | 33 | [] |
1036 | 34 | 34 | ||
1037 | 35 | >>> from lp.services.config import config | 35 | >>> from lp.services.config import config |
1038 | @@ -41,52 +41,52 @@ There should be zero entries in these feeds, since all the bugs are private. | |||
1039 | 41 | >>> browser.open('http://feeds.launchpad.test/bugs/+bugs.atom?' | 41 | >>> browser.open('http://feeds.launchpad.test/bugs/+bugs.atom?' |
1040 | 42 | ... 'field.searchtext=&search=Search+Bug+Reports&' | 42 | ... 'field.searchtext=&search=Search+Bug+Reports&' |
1041 | 43 | ... 'field.scope=all&field.scope.target=') | 43 | ... 'field.scope=all&field.scope.target=') |
1043 | 44 | >>> BSS(browser.contents)('entry') | 44 | >>> BeautifulSoup(browser.contents, 'xml')('entry') |
1044 | 45 | [] | 45 | [] |
1045 | 46 | 46 | ||
1046 | 47 | There should be just one <tr> elements for the table header in | 47 | There should be just one <tr> elements for the table header in |
1047 | 48 | these HTML feeds, since all the bugs are private. | 48 | these HTML feeds, since all the bugs are private. |
1048 | 49 | 49 | ||
1049 | 50 | >>> browser.open('http://feeds.launchpad.test/jokosher/latest-bugs.html') | 50 | >>> browser.open('http://feeds.launchpad.test/jokosher/latest-bugs.html') |
1051 | 51 | >>> len(BSS(browser.contents)('tr')) | 51 | >>> len(BeautifulSoup(browser.contents, 'xml')('tr')) |
1052 | 52 | 1 | 52 | 1 |
1053 | 53 | 53 | ||
1055 | 54 | >>> print extract_text(BSS(browser.contents)('tr')[0]) | 54 | >>> print extract_text(BeautifulSoup(browser.contents, 'xml')('tr')[0]) |
1056 | 55 | Bugs in Jokosher | 55 | Bugs in Jokosher |
1057 | 56 | 56 | ||
1058 | 57 | >>> browser.open('http://feeds.launchpad.test/mozilla/latest-bugs.html') | 57 | >>> browser.open('http://feeds.launchpad.test/mozilla/latest-bugs.html') |
1060 | 58 | >>> len(BSS(browser.contents)('tr')) | 58 | >>> len(BeautifulSoup(browser.contents, 'xml')('tr')) |
1061 | 59 | 1 | 59 | 1 |
1062 | 60 | 60 | ||
1064 | 61 | >>> print extract_text(BSS(browser.contents)('tr')[0]) | 61 | >>> print extract_text(BeautifulSoup(browser.contents, 'xml')('tr')[0]) |
1065 | 62 | Bugs in The Mozilla Project | 62 | Bugs in The Mozilla Project |
1066 | 63 | 63 | ||
1067 | 64 | >>> browser.open('http://feeds.launchpad.test/~name16/latest-bugs.html') | 64 | >>> browser.open('http://feeds.launchpad.test/~name16/latest-bugs.html') |
1069 | 65 | >>> len(BSS(browser.contents)('tr')) | 65 | >>> len(BeautifulSoup(browser.contents, 'xml')('tr')) |
1070 | 66 | 1 | 66 | 1 |
1071 | 67 | 67 | ||
1073 | 68 | >>> print extract_text(BSS(browser.contents)('tr')[0]) | 68 | >>> print extract_text(BeautifulSoup(browser.contents, 'xml')('tr')[0]) |
1074 | 69 | Bugs for Foo Bar | 69 | Bugs for Foo Bar |
1075 | 70 | 70 | ||
1076 | 71 | >>> browser.open( | 71 | >>> browser.open( |
1077 | 72 | ... 'http://feeds.launchpad.test/~simple-team/latest-bugs.html') | 72 | ... 'http://feeds.launchpad.test/~simple-team/latest-bugs.html') |
1079 | 73 | >>> len(BSS(browser.contents)('tr')) | 73 | >>> len(BeautifulSoup(browser.contents, 'xml')('tr')) |
1080 | 74 | 1 | 74 | 1 |
1081 | 75 | 75 | ||
1083 | 76 | >>> print extract_text(BSS(browser.contents)('tr')[0]) | 76 | >>> print extract_text(BeautifulSoup(browser.contents, 'xml')('tr')[0]) |
1084 | 77 | Bugs for Simple Team | 77 | Bugs for Simple Team |
1085 | 78 | 78 | ||
1086 | 79 | >>> browser.open('http://feeds.launchpad.test/bugs/+bugs.html?' | 79 | >>> browser.open('http://feeds.launchpad.test/bugs/+bugs.html?' |
1087 | 80 | ... 'field.searchtext=&search=Search+Bug+Reports&' | 80 | ... 'field.searchtext=&search=Search+Bug+Reports&' |
1088 | 81 | ... 'field.scope=all&field.scope.target=') | 81 | ... 'field.scope=all&field.scope.target=') |
1090 | 82 | >>> len(BSS(browser.contents)('tr')) | 82 | >>> len(BeautifulSoup(browser.contents, 'xml')('tr')) |
1091 | 83 | 1 | 83 | 1 |
1092 | 84 | 84 | ||
1093 | 85 | >>> try: | 85 | >>> try: |
1094 | 86 | ... browser.open('http://feeds.launchpad.test/bugs/1/bug.html') | 86 | ... browser.open('http://feeds.launchpad.test/bugs/1/bug.html') |
1095 | 87 | ... except Unauthorized: | 87 | ... except Unauthorized: |
1096 | 88 | ... print "Shouldn't raise Unauthorized exception" | 88 | ... print "Shouldn't raise Unauthorized exception" |
1098 | 89 | >>> BSS(browser.contents)('entry') | 89 | >>> BeautifulSoup(browser.contents, 'xml')('entry') |
1099 | 90 | [] | 90 | [] |
1100 | 91 | 91 | ||
1101 | 92 | Revert configuration change after tests are finished. | 92 | Revert configuration change after tests are finished. |
1102 | diff --git a/lib/lp/services/feeds/tests/helper.py b/lib/lp/services/feeds/tests/helper.py | |||
1103 | index e0a96aa..826df83 100644 | |||
1104 | --- a/lib/lp/services/feeds/tests/helper.py | |||
1105 | +++ b/lib/lp/services/feeds/tests/helper.py | |||
1106 | @@ -31,9 +31,11 @@ from zope.interface import ( | |||
1107 | 31 | implementer, | 31 | implementer, |
1108 | 32 | Interface, | 32 | Interface, |
1109 | 33 | ) | 33 | ) |
1110 | 34 | from BeautifulSoup import BeautifulStoneSoup as BSS | ||
1111 | 35 | from BeautifulSoup import SoupStrainer | ||
1112 | 36 | 34 | ||
1113 | 35 | from lp.services.beautifulsoup import ( | ||
1114 | 36 | BeautifulSoup4 as BeautifulSoup, | ||
1115 | 37 | SoupStrainer4 as SoupStrainer, | ||
1116 | 38 | ) | ||
1117 | 37 | from lp.services.webapp.publisher import LaunchpadView | 39 | from lp.services.webapp.publisher import LaunchpadView |
1118 | 38 | 40 | ||
1119 | 39 | 41 | ||
1120 | @@ -62,25 +64,23 @@ class ThingFeedView(LaunchpadView): | |||
1121 | 62 | def parse_entries(contents): | 64 | def parse_entries(contents): |
1122 | 63 | """Define a helper function for parsing feed entries.""" | 65 | """Define a helper function for parsing feed entries.""" |
1123 | 64 | strainer = SoupStrainer('entry') | 66 | strainer = SoupStrainer('entry') |
1126 | 65 | entries = [tag for tag in BSS(contents, | 67 | entries = [ |
1127 | 66 | parseOnlyThese=strainer)] | 68 | tag for tag in BeautifulSoup(contents, 'xml', parse_only=strainer)] |
1128 | 67 | return entries | 69 | return entries |
1129 | 68 | 70 | ||
1130 | 69 | 71 | ||
1131 | 70 | def parse_links(contents, rel): | 72 | def parse_links(contents, rel): |
1132 | 71 | """Define a helper function for parsing feed links.""" | 73 | """Define a helper function for parsing feed links.""" |
1133 | 72 | strainer = SoupStrainer('link', rel=rel) | 74 | strainer = SoupStrainer('link', rel=rel) |
1137 | 73 | entries = [tag for tag in BSS(contents, | 75 | entries = [ |
1138 | 74 | parseOnlyThese=strainer, | 76 | tag for tag in BeautifulSoup(contents, 'xml', parse_only=strainer)] |
1136 | 75 | selfClosingTags=['link'])] | ||
1139 | 76 | return entries | 77 | return entries |
1140 | 77 | 78 | ||
1141 | 78 | 79 | ||
1142 | 79 | def parse_ids(contents): | 80 | def parse_ids(contents): |
1143 | 80 | """Define a helper function for parsing ids.""" | 81 | """Define a helper function for parsing ids.""" |
1144 | 81 | strainer = SoupStrainer('id') | 82 | strainer = SoupStrainer('id') |
1147 | 82 | ids = [tag for tag in BSS(contents, | 83 | ids = [tag for tag in BeautifulSoup(contents, 'xml', parse_only=strainer)] |
1146 | 83 | parseOnlyThese=strainer)] | ||
1148 | 84 | return ids | 84 | return ids |
1149 | 85 | 85 | ||
1150 | 86 | 86 |
Self-approving: this is almost entirely tests, and is mechanical enough that it's not very interesting to review.