Merge lp:~abompard/mailman/sqlalchemy into lp:~raj-abhilash1/mailman/sqlalchemy
- sqlalchemy
- Merge into sqlalchemy
Status: | Needs review |
---|---|
Proposed branch: | lp:~abompard/mailman/sqlalchemy |
Merge into: | lp:~raj-abhilash1/mailman/sqlalchemy |
Diff against target: |
1466 lines (+339/-301) 28 files modified
MANIFEST.in (+1/-4) src/mailman/app/docs/moderator.rst (+29/-40) src/mailman/commands/docs/conf.rst (+1/-2) src/mailman/commands/docs/withlist.rst (+2/-2) src/mailman/config/alembic.cfg (+20/-0) src/mailman/config/config.py (+0/-2) src/mailman/config/schema.cfg (+3/-9) src/mailman/core/docs/runner.rst (+2/-0) src/mailman/core/logging.py (+28/-21) src/mailman/database/alembic/__init__.py (+4/-4) src/mailman/database/alembic/env.py (+0/-5) src/mailman/database/alembic/versions/51b7f92bd06c_initial.py (+46/-14) src/mailman/database/base.py (+5/-1) src/mailman/database/factory.py (+43/-48) src/mailman/database/tests/test_factory.py (+80/-82) src/mailman/handlers/docs/owner-recips.rst (+2/-2) src/mailman/model/docs/autorespond.rst (+11/-11) src/mailman/model/docs/mailinglist.rst (+6/-6) src/mailman/model/docs/requests.rst (+10/-10) src/mailman/model/domain.py (+4/-3) src/mailman/model/listmanager.py (+2/-1) src/mailman/model/mailinglist.py (+7/-6) src/mailman/model/user.py (+2/-2) src/mailman/rest/docs/moderation.rst (+15/-17) src/mailman/testing/layers.py (+4/-8) src/mailman/testing/testing.cfg (+1/-1) src/mailman/utilities/importer.py (+1/-0) src/mailman/utilities/tests/test_import.py (+10/-0) |
To merge this branch: | bzr merge lp:~abompard/mailman/sqlalchemy |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Abhilash Raj | Pending | ||
Review via email: mp+239952@code.launchpad.net |
Commit message
Description of the change
A small import fix
- 7287. By Aurélien Bompard
-
Fix doctests with PostgreSQL
- drop the tables after the whole testsuite has run
- don't expose DB ids in the doctests
- sort lists by list-id in the ListManager------------- This line and the following will be ignored --------------
modified:
src/mailman/app/docs/ moderator. rst
src/mailman/commands/ docs/withlist. rst
src/mailman/database/ base.py
src/mailman/model/docs/ mailinglist. rst
src/mailman/model/domain. py
src/mailman/model/listmanag er.py
src/mailman/rest/docs/ moderation. rst
src/mailman/testing/ layers. py - 7288. By Aurélien Bompard
-
Add a cleanup instruction in a doctest
- 7289. By Aurélien Bompard
-
One more sorting issue
Unmerged revisions
- 7289. By Aurélien Bompard
-
One more sorting issue
- 7288. By Aurélien Bompard
-
Add a cleanup instruction in a doctest
- 7287. By Aurélien Bompard
-
Fix doctests with PostgreSQL
- drop the tables after the whole testsuite has run
- don't expose DB ids in the doctests
- sort lists by list-id in the ListManager------------- This line and the following will be ignored --------------
modified:
src/mailman/app/docs/ moderator. rst
src/mailman/commands/ docs/withlist. rst
src/mailman/database/ base.py
src/mailman/model/docs/ mailinglist. rst
src/mailman/model/domain. py
src/mailman/model/listmanag er.py
src/mailman/rest/docs/ moderation. rst
src/mailman/testing/ layers. py - 7286. By Aurélien Bompard
-
Importer: encode_
ascii_prefixes need to be converted to bool - 7285. By Barry Warsaw
-
Use print() to smooth over the SA return of Python longs in PostgreSQL.
- 7284. By Barry Warsaw
-
Remove some unnecessary code.
- 7283. By Barry Warsaw
-
Move alembic settings to a separate alembic.cfg.
- 7282. By Barry Warsaw
-
Merge Aurélien Bompard's latest merge branch, with some cleaning up by Barry.
- 7281. By Barry Warsaw
-
Add the [logging.database] section and use it to configure the SQLAlchemy and
Alembic loggers. - 7280. By Barry Warsaw
-
Remove some unused stuff.
Preview Diff
1 | === modified file 'MANIFEST.in' | |||
2 | --- MANIFEST.in 2014-10-07 10:06:44 +0000 | |||
3 | +++ MANIFEST.in 2014-10-31 13:23:25 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
6 | 1 | include *.py *.rc | 1 | include *.py *.rc *.mako |
7 | 2 | include COPYING | 2 | include COPYING |
8 | 3 | recursive-include .buildout * | 3 | recursive-include .buildout * |
9 | 4 | recursive-include contrib * | 4 | recursive-include contrib * |
10 | @@ -6,12 +6,9 @@ | |||
11 | 6 | recursive-include data * | 6 | recursive-include data * |
12 | 7 | global-include *.txt *.rst *.po *.mo *.cfg *.sql *.zcml *.html | 7 | global-include *.txt *.rst *.po *.mo *.cfg *.sql *.zcml *.html |
13 | 8 | global-exclude *.egg-info | 8 | global-exclude *.egg-info |
14 | 9 | exclude MANIFEST.in | ||
15 | 10 | prune src/attic | 9 | prune src/attic |
16 | 11 | prune src/web | 10 | prune src/web |
17 | 12 | prune eggs | 11 | prune eggs |
18 | 13 | prune parts | 12 | prune parts |
19 | 14 | include MANIFEST.in | 13 | include MANIFEST.in |
20 | 15 | include src/mailman/testing/config.pck | 14 | include src/mailman/testing/config.pck |
21 | 16 | include src/mailman/database/alembic/script.py.mako | ||
22 | 17 | include src/mailman/database/alembic/versions/*.py | ||
23 | 18 | 15 | ||
24 | === modified file 'src/mailman/app/docs/moderator.rst' | |||
25 | --- src/mailman/app/docs/moderator.rst 2014-04-28 15:23:35 +0000 | |||
26 | +++ src/mailman/app/docs/moderator.rst 2014-10-31 13:23:25 +0000 | |||
27 | @@ -211,9 +211,8 @@ | |||
28 | 211 | ... | 211 | ... |
29 | 212 | ... Here's something important about our mailing list. | 212 | ... Here's something important about our mailing list. |
30 | 213 | ... """) | 213 | ... """) |
34 | 214 | >>> hold_message(mlist, msg, {}, 'Needs approval') | 214 | >>> req_id = hold_message(mlist, msg, {}, 'Needs approval') |
35 | 215 | 2 | 215 | >>> handle_message(mlist, req_id, Action.discard, forward=['zack@example.com']) |
33 | 216 | >>> handle_message(mlist, 2, Action.discard, forward=['zack@example.com']) | ||
36 | 217 | 216 | ||
37 | 218 | The forwarded message is in the virgin queue, destined for the moderator. | 217 | The forwarded message is in the virgin queue, destined for the moderator. |
38 | 219 | :: | 218 | :: |
39 | @@ -243,10 +242,9 @@ | |||
40 | 243 | 242 | ||
41 | 244 | >>> from mailman.app.moderator import hold_subscription | 243 | >>> from mailman.app.moderator import hold_subscription |
42 | 245 | >>> from mailman.interfaces.member import DeliveryMode | 244 | >>> from mailman.interfaces.member import DeliveryMode |
44 | 246 | >>> hold_subscription(mlist, | 245 | >>> req_id = hold_subscription(mlist, |
45 | 247 | ... 'fred@example.org', 'Fred Person', | 246 | ... 'fred@example.org', 'Fred Person', |
46 | 248 | ... '{NONE}abcxyz', DeliveryMode.regular, 'en') | 247 | ... '{NONE}abcxyz', DeliveryMode.regular, 'en') |
47 | 249 | 2 | ||
48 | 250 | 248 | ||
49 | 251 | 249 | ||
50 | 252 | Disposing of membership change requests | 250 | Disposing of membership change requests |
51 | @@ -257,26 +255,26 @@ | |||
52 | 257 | simply defer a decision for now. | 255 | simply defer a decision for now. |
53 | 258 | 256 | ||
54 | 259 | >>> from mailman.app.moderator import handle_subscription | 257 | >>> from mailman.app.moderator import handle_subscription |
57 | 260 | >>> handle_subscription(mlist, 2, Action.defer) | 258 | >>> handle_subscription(mlist, req_id, Action.defer) |
58 | 261 | >>> requests.get_request(2) is not None | 259 | >>> requests.get_request(req_id) is not None |
59 | 262 | True | 260 | True |
60 | 263 | 261 | ||
61 | 264 | The held subscription can also be discarded. | 262 | The held subscription can also be discarded. |
62 | 265 | 263 | ||
65 | 266 | >>> handle_subscription(mlist, 2, Action.discard) | 264 | >>> handle_subscription(mlist, req_id, Action.discard) |
66 | 267 | >>> print(requests.get_request(2)) | 265 | >>> print(requests.get_request(req_id)) |
67 | 268 | None | 266 | None |
68 | 269 | 267 | ||
69 | 270 | Gwen tries to subscribe to the mailing list, but... | 268 | Gwen tries to subscribe to the mailing list, but... |
70 | 271 | 269 | ||
72 | 272 | >>> hold_subscription(mlist, | 270 | >>> req_id = hold_subscription(mlist, |
73 | 273 | ... 'gwen@example.org', 'Gwen Person', | 271 | ... 'gwen@example.org', 'Gwen Person', |
74 | 274 | ... '{NONE}zyxcba', DeliveryMode.regular, 'en') | 272 | ... '{NONE}zyxcba', DeliveryMode.regular, 'en') |
76 | 275 | 2 | 273 | |
77 | 276 | 274 | ||
78 | 277 | ...her request is rejected... | 275 | ...her request is rejected... |
79 | 278 | 276 | ||
81 | 279 | >>> handle_subscription(mlist, 2, Action.reject, 'This is a closed list') | 277 | >>> handle_subscription(mlist, req_id, Action.reject, 'This is a closed list') |
82 | 280 | >>> messages = get_queue_messages('virgin') | 278 | >>> messages = get_queue_messages('virgin') |
83 | 281 | >>> len(messages) | 279 | >>> len(messages) |
84 | 282 | 1 | 280 | 1 |
85 | @@ -304,14 +302,13 @@ | |||
86 | 304 | mailing list. | 302 | mailing list. |
87 | 305 | 303 | ||
88 | 306 | >>> mlist.send_welcome_message = False | 304 | >>> mlist.send_welcome_message = False |
90 | 307 | >>> hold_subscription(mlist, | 305 | >>> req_id = hold_subscription(mlist, |
91 | 308 | ... 'herb@example.org', 'Herb Person', | 306 | ... 'herb@example.org', 'Herb Person', |
92 | 309 | ... 'abcxyz', DeliveryMode.regular, 'en') | 307 | ... 'abcxyz', DeliveryMode.regular, 'en') |
93 | 310 | 2 | ||
94 | 311 | 308 | ||
95 | 312 | The moderators accept the subscription request. | 309 | The moderators accept the subscription request. |
96 | 313 | 310 | ||
98 | 314 | >>> handle_subscription(mlist, 2, Action.accept) | 311 | >>> handle_subscription(mlist, req_id, Action.accept) |
99 | 315 | 312 | ||
100 | 316 | And now Herb is a member of the mailing list. | 313 | And now Herb is a member of the mailing list. |
101 | 317 | 314 | ||
102 | @@ -328,29 +325,27 @@ | |||
103 | 328 | Herb now wants to leave the mailing list, but his request must be approved. | 325 | Herb now wants to leave the mailing list, but his request must be approved. |
104 | 329 | 326 | ||
105 | 330 | >>> from mailman.app.moderator import hold_unsubscription | 327 | >>> from mailman.app.moderator import hold_unsubscription |
108 | 331 | >>> hold_unsubscription(mlist, 'herb@example.org') | 328 | >>> req_id = hold_unsubscription(mlist, 'herb@example.org') |
107 | 332 | 2 | ||
109 | 333 | 329 | ||
110 | 334 | As with subscription requests, the unsubscription request can be deferred. | 330 | As with subscription requests, the unsubscription request can be deferred. |
111 | 335 | 331 | ||
112 | 336 | >>> from mailman.app.moderator import handle_unsubscription | 332 | >>> from mailman.app.moderator import handle_unsubscription |
114 | 337 | >>> handle_unsubscription(mlist, 2, Action.defer) | 333 | >>> handle_unsubscription(mlist, req_id, Action.defer) |
115 | 338 | >>> print(mlist.members.get_member('herb@example.org').address) | 334 | >>> print(mlist.members.get_member('herb@example.org').address) |
116 | 339 | Herb Person <herb@example.org> | 335 | Herb Person <herb@example.org> |
117 | 340 | 336 | ||
118 | 341 | The held unsubscription can also be discarded, and the member will remain | 337 | The held unsubscription can also be discarded, and the member will remain |
119 | 342 | subscribed. | 338 | subscribed. |
120 | 343 | 339 | ||
122 | 344 | >>> handle_unsubscription(mlist, 2, Action.discard) | 340 | >>> handle_unsubscription(mlist, req_id, Action.discard) |
123 | 345 | >>> print(mlist.members.get_member('herb@example.org').address) | 341 | >>> print(mlist.members.get_member('herb@example.org').address) |
124 | 346 | Herb Person <herb@example.org> | 342 | Herb Person <herb@example.org> |
125 | 347 | 343 | ||
126 | 348 | The request can be rejected, in which case a message is sent to the member, | 344 | The request can be rejected, in which case a message is sent to the member, |
127 | 349 | and the person remains a member of the mailing list. | 345 | and the person remains a member of the mailing list. |
128 | 350 | 346 | ||
132 | 351 | >>> hold_unsubscription(mlist, 'herb@example.org') | 347 | >>> req_id = hold_unsubscription(mlist, 'herb@example.org') |
133 | 352 | 2 | 348 | >>> handle_unsubscription(mlist, req_id, Action.reject, 'No can do') |
131 | 353 | >>> handle_unsubscription(mlist, 2, Action.reject, 'No can do') | ||
134 | 354 | >>> print(mlist.members.get_member('herb@example.org').address) | 349 | >>> print(mlist.members.get_member('herb@example.org').address) |
135 | 355 | Herb Person <herb@example.org> | 350 | Herb Person <herb@example.org> |
136 | 356 | 351 | ||
137 | @@ -381,10 +376,9 @@ | |||
138 | 381 | The unsubscription request can also be accepted. This removes the member from | 376 | The unsubscription request can also be accepted. This removes the member from |
139 | 382 | the mailing list. | 377 | the mailing list. |
140 | 383 | 378 | ||
143 | 384 | >>> hold_unsubscription(mlist, 'herb@example.org') | 379 | >>> req_id = hold_unsubscription(mlist, 'herb@example.org') |
142 | 385 | 2 | ||
144 | 386 | >>> mlist.send_goodbye_message = False | 380 | >>> mlist.send_goodbye_message = False |
146 | 387 | >>> handle_unsubscription(mlist, 2, Action.accept) | 381 | >>> handle_unsubscription(mlist, req_id, Action.accept) |
147 | 388 | >>> print(mlist.members.get_member('herb@example.org')) | 382 | >>> print(mlist.members.get_member('herb@example.org')) |
148 | 389 | None | 383 | None |
149 | 390 | 384 | ||
150 | @@ -403,9 +397,8 @@ | |||
151 | 403 | 397 | ||
152 | 404 | Iris tries to subscribe to the mailing list. | 398 | Iris tries to subscribe to the mailing list. |
153 | 405 | 399 | ||
155 | 406 | >>> hold_subscription(mlist, 'iris@example.org', 'Iris Person', | 400 | >>> req_id = hold_subscription(mlist, 'iris@example.org', 'Iris Person', |
156 | 407 | ... 'password', DeliveryMode.regular, 'en') | 401 | ... 'password', DeliveryMode.regular, 'en') |
157 | 408 | 2 | ||
158 | 409 | 402 | ||
159 | 410 | There's now a message in the virgin queue, destined for the list owner. | 403 | There's now a message in the virgin queue, destined for the list owner. |
160 | 411 | 404 | ||
161 | @@ -429,8 +422,7 @@ | |||
162 | 429 | Similarly, the administrator gets notifications on unsubscription requests. | 422 | Similarly, the administrator gets notifications on unsubscription requests. |
163 | 430 | Jeff is a member of the mailing list, and chooses to unsubscribe. | 423 | Jeff is a member of the mailing list, and chooses to unsubscribe. |
164 | 431 | 424 | ||
167 | 432 | >>> hold_unsubscription(mlist, 'jeff@example.org') | 425 | >>> unsub_req_id = hold_unsubscription(mlist, 'jeff@example.org') |
166 | 433 | 3 | ||
168 | 434 | >>> messages = get_queue_messages('virgin') | 426 | >>> messages = get_queue_messages('virgin') |
169 | 435 | >>> len(messages) | 427 | >>> len(messages) |
170 | 436 | 1 | 428 | 1 |
171 | @@ -457,7 +449,7 @@ | |||
172 | 457 | 449 | ||
173 | 458 | >>> mlist.admin_notify_mchanges = True | 450 | >>> mlist.admin_notify_mchanges = True |
174 | 459 | >>> mlist.admin_immed_notify = False | 451 | >>> mlist.admin_immed_notify = False |
176 | 460 | >>> handle_subscription(mlist, 2, Action.accept) | 452 | >>> handle_subscription(mlist, req_id, Action.accept) |
177 | 461 | >>> messages = get_queue_messages('virgin') | 453 | >>> messages = get_queue_messages('virgin') |
178 | 462 | >>> len(messages) | 454 | >>> len(messages) |
179 | 463 | 1 | 455 | 1 |
180 | @@ -474,9 +466,8 @@ | |||
181 | 474 | Similarly when an unsubscription request is accepted, the administrators can | 466 | Similarly when an unsubscription request is accepted, the administrators can |
182 | 475 | get a notification. | 467 | get a notification. |
183 | 476 | 468 | ||
187 | 477 | >>> hold_unsubscription(mlist, 'iris@example.org') | 469 | >>> req_id = hold_unsubscription(mlist, 'iris@example.org') |
188 | 478 | 4 | 470 | >>> handle_unsubscription(mlist, req_id, Action.accept) |
186 | 479 | >>> handle_unsubscription(mlist, 4, Action.accept) | ||
189 | 480 | >>> messages = get_queue_messages('virgin') | 471 | >>> messages = get_queue_messages('virgin') |
190 | 481 | >>> len(messages) | 472 | >>> len(messages) |
191 | 482 | 1 | 473 | 1 |
192 | @@ -498,10 +489,9 @@ | |||
193 | 498 | 489 | ||
194 | 499 | >>> mlist.admin_notify_mchanges = False | 490 | >>> mlist.admin_notify_mchanges = False |
195 | 500 | >>> mlist.send_welcome_message = True | 491 | >>> mlist.send_welcome_message = True |
200 | 501 | >>> hold_subscription(mlist, 'kate@example.org', 'Kate Person', | 492 | >>> req_id = hold_subscription(mlist, 'kate@example.org', 'Kate Person', |
201 | 502 | ... 'password', DeliveryMode.regular, 'en') | 493 | ... 'password', DeliveryMode.regular, 'en') |
202 | 503 | 4 | 494 | >>> handle_subscription(mlist, req_id, Action.accept) |
199 | 504 | >>> handle_subscription(mlist, 4, Action.accept) | ||
203 | 505 | >>> messages = get_queue_messages('virgin') | 495 | >>> messages = get_queue_messages('virgin') |
204 | 506 | >>> len(messages) | 496 | >>> len(messages) |
205 | 507 | 1 | 497 | 1 |
206 | @@ -523,9 +513,8 @@ | |||
207 | 523 | goodbye message. | 513 | goodbye message. |
208 | 524 | 514 | ||
209 | 525 | >>> mlist.send_goodbye_message = True | 515 | >>> mlist.send_goodbye_message = True |
213 | 526 | >>> hold_unsubscription(mlist, 'kate@example.org') | 516 | >>> req_id = hold_unsubscription(mlist, 'kate@example.org') |
214 | 527 | 4 | 517 | >>> handle_unsubscription(mlist, req_id, Action.accept) |
212 | 528 | >>> handle_unsubscription(mlist, 4, Action.accept) | ||
215 | 529 | >>> messages = get_queue_messages('virgin') | 518 | >>> messages = get_queue_messages('virgin') |
216 | 530 | >>> len(messages) | 519 | >>> len(messages) |
217 | 531 | 1 | 520 | 1 |
218 | 532 | 521 | ||
219 | === modified file 'src/mailman/commands/docs/conf.rst' | |||
220 | --- src/mailman/commands/docs/conf.rst 2014-10-10 04:59:43 +0000 | |||
221 | +++ src/mailman/commands/docs/conf.rst 2014-10-31 13:23:25 +0000 | |||
222 | @@ -22,7 +22,7 @@ | |||
223 | 22 | command without any options. | 22 | command without any options. |
224 | 23 | 23 | ||
225 | 24 | >>> command.process(FakeArgs) | 24 | >>> command.process(FakeArgs) |
227 | 25 | [alembic] script_location: mailman.database:alembic | 25 | [logging.archiver] path: mailman.log |
228 | 26 | ... | 26 | ... |
229 | 27 | [passwords] password_length: 8 | 27 | [passwords] password_length: 8 |
230 | 28 | ... | 28 | ... |
231 | @@ -43,7 +43,6 @@ | |||
232 | 43 | >>> FakeArgs.section = None | 43 | >>> FakeArgs.section = None |
233 | 44 | >>> FakeArgs.key = 'path' | 44 | >>> FakeArgs.key = 'path' |
234 | 45 | >>> command.process(FakeArgs) | 45 | >>> command.process(FakeArgs) |
235 | 46 | [logging.dbmigration] path: mailman.log | ||
236 | 47 | [logging.archiver] path: mailman.log | 46 | [logging.archiver] path: mailman.log |
237 | 48 | [logging.locks] path: mailman.log | 47 | [logging.locks] path: mailman.log |
238 | 49 | [logging.mischief] path: mailman.log | 48 | [logging.mischief] path: mailman.log |
239 | 50 | 49 | ||
240 | === modified file 'src/mailman/commands/docs/withlist.rst' | |||
241 | --- src/mailman/commands/docs/withlist.rst 2014-04-28 15:23:35 +0000 | |||
242 | +++ src/mailman/commands/docs/withlist.rst 2014-10-31 13:23:25 +0000 | |||
243 | @@ -90,13 +90,13 @@ | |||
244 | 90 | >>> args.listname = '^.*example.com' | 90 | >>> args.listname = '^.*example.com' |
245 | 91 | >>> command.process(args) | 91 | >>> command.process(args) |
246 | 92 | The list's display name is Aardvark | 92 | The list's display name is Aardvark |
247 | 93 | The list's display name is Badboys | ||
248 | 93 | The list's display name is Badger | 94 | The list's display name is Badger |
249 | 94 | The list's display name is Badboys | ||
250 | 95 | 95 | ||
251 | 96 | >>> args.listname = '^bad.*' | 96 | >>> args.listname = '^bad.*' |
252 | 97 | >>> command.process(args) | 97 | >>> command.process(args) |
253 | 98 | The list's display name is Badboys | ||
254 | 98 | The list's display name is Badger | 99 | The list's display name is Badger |
255 | 99 | The list's display name is Badboys | ||
256 | 100 | 100 | ||
257 | 101 | >>> args.listname = '^foo' | 101 | >>> args.listname = '^foo' |
258 | 102 | >>> command.process(args) | 102 | >>> command.process(args) |
259 | 103 | 103 | ||
260 | === added file 'src/mailman/config/alembic.cfg' | |||
261 | --- src/mailman/config/alembic.cfg 1970-01-01 00:00:00 +0000 | |||
262 | +++ src/mailman/config/alembic.cfg 2014-10-31 13:23:25 +0000 | |||
263 | @@ -0,0 +1,20 @@ | |||
264 | 1 | # Copyright (C) 2014 by the Free Software Foundation, Inc. | ||
265 | 2 | # | ||
266 | 3 | # This file is part of GNU Mailman. | ||
267 | 4 | # | ||
268 | 5 | # GNU Mailman is free software: you can redistribute it and/or modify it under | ||
269 | 6 | # the terms of the GNU General Public License as published by the Free | ||
270 | 7 | # Software Foundation, either version 3 of the License, or (at your option) | ||
271 | 8 | # any later version. | ||
272 | 9 | # | ||
273 | 10 | # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT | ||
274 | 11 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
275 | 12 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
276 | 13 | # more details. | ||
277 | 14 | # | ||
278 | 15 | # You should have received a copy of the GNU General Public License along with | ||
279 | 16 | # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. | ||
280 | 17 | |||
281 | 18 | [alembic] | ||
282 | 19 | # Path to Alembic migration scripts. | ||
283 | 20 | script_location: mailman.database:alembic | ||
284 | 0 | 21 | ||
285 | === modified file 'src/mailman/config/config.py' | |||
286 | --- src/mailman/config/config.py 2014-10-02 14:46:00 +0000 | |||
287 | +++ src/mailman/config/config.py 2014-10-31 13:23:25 +0000 | |||
288 | @@ -87,7 +87,6 @@ | |||
289 | 87 | self.pipelines = {} | 87 | self.pipelines = {} |
290 | 88 | self.commands = {} | 88 | self.commands = {} |
291 | 89 | self.password_context = None | 89 | self.password_context = None |
292 | 90 | self.initialized = False | ||
293 | 91 | 90 | ||
294 | 92 | def _clear(self): | 91 | def _clear(self): |
295 | 93 | """Clear the cached configuration variables.""" | 92 | """Clear the cached configuration variables.""" |
296 | @@ -137,7 +136,6 @@ | |||
297 | 137 | # Expand and set up all directories. | 136 | # Expand and set up all directories. |
298 | 138 | self._expand_paths() | 137 | self._expand_paths() |
299 | 139 | self.ensure_directories_exist() | 138 | self.ensure_directories_exist() |
300 | 140 | self.initialized = True | ||
301 | 141 | notify(ConfigurationUpdatedEvent(self)) | 139 | notify(ConfigurationUpdatedEvent(self)) |
302 | 142 | 140 | ||
303 | 143 | def _expand_paths(self): | 141 | def _expand_paths(self): |
304 | 144 | 142 | ||
305 | === modified file 'src/mailman/config/schema.cfg' | |||
306 | --- src/mailman/config/schema.cfg 2014-10-10 04:59:43 +0000 | |||
307 | +++ src/mailman/config/schema.cfg 2014-10-31 13:23:25 +0000 | |||
308 | @@ -226,6 +226,7 @@ | |||
309 | 226 | # - archiver -- All archiver output | 226 | # - archiver -- All archiver output |
310 | 227 | # - bounce -- All bounce processing logs go here | 227 | # - bounce -- All bounce processing logs go here |
311 | 228 | # - config -- Configuration issues | 228 | # - config -- Configuration issues |
312 | 229 | # - database -- Database logging (SQLAlchemy and Alembic) | ||
313 | 229 | # - debug -- Only used for development | 230 | # - debug -- Only used for development |
314 | 230 | # - error -- All exceptions go to this log | 231 | # - error -- All exceptions go to this log |
315 | 231 | # - fromusenet -- Information related to the Usenet to Mailman gateway | 232 | # - fromusenet -- Information related to the Usenet to Mailman gateway |
316 | @@ -237,8 +238,6 @@ | |||
317 | 237 | # - smtp-failure -- Unsuccessful SMTP activity | 238 | # - smtp-failure -- Unsuccessful SMTP activity |
318 | 238 | # - subscribe -- Information about leaves/joins | 239 | # - subscribe -- Information about leaves/joins |
319 | 239 | # - vette -- Message vetting information | 240 | # - vette -- Message vetting information |
320 | 240 | # - database -- Database activity | ||
321 | 241 | # - dbmigration -- Database migrations | ||
322 | 242 | format: %(asctime)s (%(process)d) %(message)s | 241 | format: %(asctime)s (%(process)d) %(message)s |
323 | 243 | datefmt: %b %d %H:%M:%S %Y | 242 | datefmt: %b %d %H:%M:%S %Y |
324 | 244 | propagate: no | 243 | propagate: no |
325 | @@ -254,6 +253,8 @@ | |||
326 | 254 | 253 | ||
327 | 255 | [logging.config] | 254 | [logging.config] |
328 | 256 | 255 | ||
329 | 256 | [logging.database] | ||
330 | 257 | |||
331 | 257 | [logging.debug] | 258 | [logging.debug] |
332 | 258 | path: debug.log | 259 | path: debug.log |
333 | 259 | level: info | 260 | level: info |
334 | @@ -306,9 +307,6 @@ | |||
335 | 306 | [logging.database] | 307 | [logging.database] |
336 | 307 | level: warn | 308 | level: warn |
337 | 308 | 309 | ||
338 | 309 | [logging.dbmigration] | ||
339 | 310 | level: warn | ||
340 | 311 | |||
341 | 312 | 310 | ||
342 | 313 | [webservice] | 311 | [webservice] |
343 | 314 | # The hostname at which admin web service resources are exposed. | 312 | # The hostname at which admin web service resources are exposed. |
344 | @@ -645,7 +643,3 @@ | |||
345 | 645 | CC X-Original-CC | 643 | CC X-Original-CC |
346 | 646 | Content-Transfer-Encoding X-Original-Content-Transfer-Encoding | 644 | Content-Transfer-Encoding X-Original-Content-Transfer-Encoding |
347 | 647 | MIME-Version X-MIME-Version | 645 | MIME-Version X-MIME-Version |
348 | 648 | |||
349 | 649 | [alembic] | ||
350 | 650 | # path to migration scripts | ||
351 | 651 | script_location = mailman.database:alembic | ||
352 | 652 | 646 | ||
353 | === modified file 'src/mailman/core/docs/runner.rst' | |||
354 | --- src/mailman/core/docs/runner.rst 2014-04-28 15:23:35 +0000 | |||
355 | +++ src/mailman/core/docs/runner.rst 2014-10-31 13:23:25 +0000 | |||
356 | @@ -73,3 +73,5 @@ | |||
357 | 73 | version : 3 | 73 | version : 3 |
358 | 74 | 74 | ||
359 | 75 | XXX More of the Runner API should be tested. | 75 | XXX More of the Runner API should be tested. |
360 | 76 | |||
361 | 77 | >>> config.pop('test-runner') | ||
362 | 76 | 78 | ||
363 | === modified file 'src/mailman/core/logging.py' | |||
364 | --- src/mailman/core/logging.py 2014-10-07 09:19:20 +0000 | |||
365 | +++ src/mailman/core/logging.py 2014-10-31 13:23:25 +0000 | |||
366 | @@ -104,6 +104,27 @@ | |||
367 | 104 | 104 | ||
368 | 105 | 105 | ||
369 | 106 | 106 | ||
370 | 107 | 107 | ||
371 | 108 | def _init_logger(propagate, sub_name, log, logger_config): | ||
372 | 109 | # Get settings from log configuration file (or defaults). | ||
373 | 110 | log_format = logger_config.format | ||
374 | 111 | log_datefmt = logger_config.datefmt | ||
375 | 112 | # Propagation to the root logger is how we handle logging to stderr | ||
376 | 113 | # when the runners are not run as a subprocess of 'bin/mailman start'. | ||
377 | 114 | log.propagate = (as_boolean(logger_config.propagate) | ||
378 | 115 | if propagate is None else propagate) | ||
379 | 116 | # Set the logger's level. | ||
380 | 117 | log.setLevel(as_log_level(logger_config.level)) | ||
381 | 118 | # Create a formatter for this logger, then a handler, and link the | ||
382 | 119 | # formatter to the handler. | ||
383 | 120 | formatter = logging.Formatter(fmt=log_format, datefmt=log_datefmt) | ||
384 | 121 | path_str = logger_config.path | ||
385 | 122 | path_abs = os.path.normpath(os.path.join(config.LOG_DIR, path_str)) | ||
386 | 123 | handler = ReopenableFileHandler(sub_name, path_abs) | ||
387 | 124 | _handlers[sub_name] = handler | ||
388 | 125 | handler.setFormatter(formatter) | ||
389 | 126 | log.addHandler(handler) | ||
390 | 127 | |||
391 | 128 | |||
392 | 108 | def initialize(propagate=None): | 129 | def initialize(propagate=None): |
393 | 109 | """Initialize all logs. | 130 | """Initialize all logs. |
394 | 110 | 131 | ||
395 | @@ -126,32 +147,18 @@ | |||
396 | 126 | continue | 147 | continue |
397 | 127 | if sub_name == 'locks': | 148 | if sub_name == 'locks': |
398 | 128 | log = logging.getLogger('flufl.lock') | 149 | log = logging.getLogger('flufl.lock') |
400 | 129 | elif sub_name == 'database': | 150 | if sub_name == 'database': |
401 | 151 | # Set both the SQLAlchemy and Alembic logs to the mailman.database | ||
402 | 152 | # log configuration, essentially ignoring the alembic.cfg | ||
403 | 153 | # settings. Do the SQLAlchemy one first, then let the Alembic one | ||
404 | 154 | # fall through to the common code path. | ||
405 | 130 | log = logging.getLogger('sqlalchemy') | 155 | log = logging.getLogger('sqlalchemy') |
407 | 131 | elif sub_name == 'dbmigration': | 156 | _init_logger(propagate, sub_name, log, logger_config) |
408 | 132 | log = logging.getLogger('alembic') | 157 | log = logging.getLogger('alembic') |
409 | 133 | else: | 158 | else: |
410 | 134 | logger_name = 'mailman.' + sub_name | 159 | logger_name = 'mailman.' + sub_name |
411 | 135 | log = logging.getLogger(logger_name) | 160 | log = logging.getLogger(logger_name) |
431 | 136 | # Get settings from log configuration file (or defaults). | 161 | _init_logger(propagate, sub_name, log, logger_config) |
413 | 137 | log_format = logger_config.format | ||
414 | 138 | log_datefmt = logger_config.datefmt | ||
415 | 139 | # Propagation to the root logger is how we handle logging to stderr | ||
416 | 140 | # when the runners are not run as a subprocess of 'bin/mailman start'. | ||
417 | 141 | log.propagate = (as_boolean(logger_config.propagate) | ||
418 | 142 | if propagate is None else propagate) | ||
419 | 143 | # Set the logger's level. | ||
420 | 144 | log.setLevel(as_log_level(logger_config.level)) | ||
421 | 145 | # Create a formatter for this logger, then a handler, and link the | ||
422 | 146 | # formatter to the handler. | ||
423 | 147 | formatter = logging.Formatter(fmt=log_format, datefmt=log_datefmt) | ||
424 | 148 | path_str = logger_config.path | ||
425 | 149 | path_abs = os.path.normpath(os.path.join(config.LOG_DIR, path_str)) | ||
426 | 150 | handler = ReopenableFileHandler(sub_name, path_abs) | ||
427 | 151 | _handlers[sub_name] = handler | ||
428 | 152 | handler.setFormatter(formatter) | ||
429 | 153 | log.addHandler(handler) | ||
430 | 154 | |||
432 | 155 | 162 | ||
433 | 156 | 163 | ||
434 | 157 | 164 | ||
435 | 158 | def reopen(): | 165 | def reopen(): |
436 | 159 | 166 | ||
437 | === modified file 'src/mailman/database/alembic/__init__.py' | |||
438 | --- src/mailman/database/alembic/__init__.py 2014-10-10 04:59:43 +0000 | |||
439 | +++ src/mailman/database/alembic/__init__.py 2014-10-31 13:23:25 +0000 | |||
440 | @@ -15,18 +15,18 @@ | |||
441 | 15 | # You should have received a copy of the GNU General Public License along with | 15 | # You should have received a copy of the GNU General Public License along with |
442 | 16 | # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. | 16 | # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. |
443 | 17 | 17 | ||
445 | 18 | "Alembic config init." | 18 | """Alembic configuration initization.""" |
446 | 19 | 19 | ||
447 | 20 | from __future__ import absolute_import, print_function, unicode_literals | 20 | from __future__ import absolute_import, print_function, unicode_literals |
448 | 21 | 21 | ||
449 | 22 | __metaclass__ = type | 22 | __metaclass__ = type |
450 | 23 | __all__ = [ | 23 | __all__ = [ |
453 | 24 | 'alembic_cfg' | 24 | 'alembic_cfg', |
454 | 25 | ] | 25 | ] |
455 | 26 | 26 | ||
456 | 27 | 27 | ||
457 | 28 | from alembic.config import Config | 28 | from alembic.config import Config |
458 | 29 | from mailman.utilities.modules import expand_path | 29 | from mailman.utilities.modules import expand_path |
459 | 30 | 30 | ||
460 | 31 | 31 | ||
462 | 32 | alembic_cfg=Config(expand_path("python:mailman.config.schema")) | 32 | alembic_cfg = Config(expand_path('python:mailman.config.alembic')) |
463 | 33 | 33 | ||
464 | === modified file 'src/mailman/database/alembic/env.py' | |||
465 | --- src/mailman/database/alembic/env.py 2014-10-10 04:59:43 +0000 | |||
466 | +++ src/mailman/database/alembic/env.py 2014-10-31 13:23:25 +0000 | |||
467 | @@ -30,15 +30,10 @@ | |||
468 | 30 | from contextlib import closing | 30 | from contextlib import closing |
469 | 31 | from sqlalchemy import create_engine | 31 | from sqlalchemy import create_engine |
470 | 32 | 32 | ||
471 | 33 | from mailman.core import initialize | ||
472 | 34 | from mailman.config import config | 33 | from mailman.config import config |
473 | 35 | from mailman.database.alembic import alembic_cfg | ||
474 | 36 | from mailman.database.model import Model | 34 | from mailman.database.model import Model |
475 | 37 | from mailman.utilities.string import expand | 35 | from mailman.utilities.string import expand |
476 | 38 | 36 | ||
477 | 39 | if not config.initialized: | ||
478 | 40 | initialize.initialize_1(context.config.config_file_name) | ||
479 | 41 | |||
480 | 42 | 37 | ||
481 | 43 | 38 | ||
482 | 44 | 39 | ||
483 | 45 | def run_migrations_offline(): | 40 | def run_migrations_offline(): |
484 | 46 | 41 | ||
485 | === modified file 'src/mailman/database/alembic/versions/51b7f92bd06c_initial.py' | |||
486 | --- src/mailman/database/alembic/versions/51b7f92bd06c_initial.py 2014-10-10 16:58:48 +0000 | |||
487 | +++ src/mailman/database/alembic/versions/51b7f92bd06c_initial.py 2014-10-31 13:23:25 +0000 | |||
488 | @@ -1,34 +1,66 @@ | |||
490 | 1 | """initial | 1 | # Copyright (C) 2014 by the Free Software Foundation, Inc. |
491 | 2 | # | ||
492 | 3 | # This file is part of GNU Mailman. | ||
493 | 4 | # | ||
494 | 5 | # GNU Mailman is free software: you can redistribute it and/or modify it under | ||
495 | 6 | # the terms of the GNU General Public License as published by the Free | ||
496 | 7 | # Software Foundation, either version 3 of the License, or (at your option) | ||
497 | 8 | # any later version. | ||
498 | 9 | # | ||
499 | 10 | # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT | ||
500 | 11 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
501 | 12 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
502 | 13 | # more details. | ||
503 | 14 | # | ||
504 | 15 | # You should have received a copy of the GNU General Public License along with | ||
505 | 16 | # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. | ||
506 | 17 | |||
507 | 18 | """Initial migration. | ||
508 | 19 | |||
509 | 20 | This empty migration file makes sure there is always an alembic_version | ||
510 | 21 | in the database. As a consequence, if the database version is reported | ||
511 | 22 | as None, it means the database needs to be created from scratch with | ||
512 | 23 | SQLAlchemy itself. | ||
513 | 24 | |||
514 | 25 | It also removes schema items left over from Storm. | ||
515 | 2 | 26 | ||
516 | 3 | Revision ID: 51b7f92bd06c | 27 | Revision ID: 51b7f92bd06c |
517 | 4 | Revises: None | 28 | Revises: None |
518 | 5 | Create Date: 2014-10-10 09:53:35.624472 | 29 | Create Date: 2014-10-10 09:53:35.624472 |
519 | 6 | |||
520 | 7 | """ | 30 | """ |
521 | 8 | 31 | ||
523 | 9 | # revision identifiers, used by Alembic. | 32 | from __future__ import absolute_import, print_function, unicode_literals |
524 | 33 | |||
525 | 34 | __metaclass__ = type | ||
526 | 35 | __all__ = [ | ||
527 | 36 | 'downgrade', | ||
528 | 37 | 'upgrade', | ||
529 | 38 | ] | ||
530 | 39 | |||
531 | 40 | |||
532 | 41 | from alembic import op | ||
533 | 42 | import sqlalchemy as sa | ||
534 | 43 | |||
535 | 44 | |||
536 | 45 | # Revision identifiers, used by Alembic. | ||
537 | 10 | revision = '51b7f92bd06c' | 46 | revision = '51b7f92bd06c' |
538 | 11 | down_revision = None | 47 | down_revision = None |
539 | 12 | 48 | ||
540 | 13 | from alembic import op | ||
541 | 14 | import sqlalchemy as sa | ||
542 | 15 | |||
543 | 16 | 49 | ||
544 | 17 | def upgrade(): | 50 | def upgrade(): |
545 | 18 | ### commands auto generated by Alembic - please adjust! ### | ||
546 | 19 | op.drop_table('version') | 51 | op.drop_table('version') |
549 | 20 | if op.get_bind().dialect.name != "sqlite": | 52 | if op.get_bind().dialect.name != 'sqlite': |
550 | 21 | # SQLite does not support dropping columns | 53 | # SQLite does not support dropping columns. |
551 | 22 | op.drop_column('mailinglist', 'acceptable_aliases_id') | 54 | op.drop_column('mailinglist', 'acceptable_aliases_id') |
553 | 23 | op.create_index(op.f('ix_user__user_id'), 'user', ['_user_id'], unique=False) | 55 | op.create_index(op.f('ix_user__user_id'), 'user', |
554 | 56 | ['_user_id'], unique=False) | ||
555 | 24 | op.drop_index('ix_user_user_id', table_name='user') | 57 | op.drop_index('ix_user_user_id', table_name='user') |
556 | 25 | ### end Alembic commands ### | ||
557 | 26 | 58 | ||
558 | 27 | 59 | ||
559 | 28 | def downgrade(): | 60 | def downgrade(): |
560 | 29 | ### commands auto generated by Alembic - please adjust! ### | ||
561 | 30 | op.create_table('version') | 61 | op.create_table('version') |
562 | 31 | op.create_index('ix_user_user_id', 'user', ['_user_id'], unique=False) | 62 | op.create_index('ix_user_user_id', 'user', ['_user_id'], unique=False) |
563 | 32 | op.drop_index(op.f('ix_user__user_id'), table_name='user') | 63 | op.drop_index(op.f('ix_user__user_id'), table_name='user') |
566 | 33 | op.add_column('mailinglist', sa.Column('acceptable_aliases_id', sa.INTEGER(), nullable=True)) | 64 | op.add_column( |
567 | 34 | ### end Alembic commands ### | 65 | 'mailinglist', |
568 | 66 | sa.Column('acceptable_aliases_id', sa.INTEGER(), nullable=True)) | ||
569 | 35 | 67 | ||
570 | === modified file 'src/mailman/database/base.py' | |||
571 | --- src/mailman/database/base.py 2014-10-06 13:58:58 +0000 | |||
572 | +++ src/mailman/database/base.py 2014-10-31 13:23:25 +0000 | |||
573 | @@ -25,7 +25,6 @@ | |||
574 | 25 | 25 | ||
575 | 26 | import logging | 26 | import logging |
576 | 27 | 27 | ||
577 | 28 | from alembic import command | ||
578 | 29 | from sqlalchemy import create_engine | 28 | from sqlalchemy import create_engine |
579 | 30 | from sqlalchemy.orm import sessionmaker | 29 | from sqlalchemy.orm import sessionmaker |
580 | 31 | from zope.interface import implementer | 30 | from zope.interface import implementer |
581 | @@ -115,3 +114,8 @@ | |||
582 | 115 | session = sessionmaker(bind=self.engine) | 114 | session = sessionmaker(bind=self.engine) |
583 | 116 | self.store = session() | 115 | self.store = session() |
584 | 117 | self.store.commit() | 116 | self.store.commit() |
585 | 117 | |||
586 | 118 | def destroy(self): | ||
587 | 119 | """Drop all database tables""" | ||
588 | 120 | from mailman.database.model import Model | ||
589 | 121 | Model.metadata.drop_all(self.engine) | ||
590 | 118 | 122 | ||
591 | === modified file 'src/mailman/database/factory.py' | |||
592 | --- src/mailman/database/factory.py 2014-10-10 16:44:01 +0000 | |||
593 | +++ src/mailman/database/factory.py 2014-10-31 13:23:25 +0000 | |||
594 | @@ -28,8 +28,8 @@ | |||
595 | 28 | 28 | ||
596 | 29 | import os | 29 | import os |
597 | 30 | import types | 30 | import types |
598 | 31 | import alembic.command | ||
599 | 31 | 32 | ||
600 | 32 | from alembic import command | ||
601 | 33 | from alembic.migration import MigrationContext | 33 | from alembic.migration import MigrationContext |
602 | 34 | from alembic.script import ScriptDirectory | 34 | from alembic.script import ScriptDirectory |
603 | 35 | from flufl.lock import Lock | 35 | from flufl.lock import Lock |
604 | @@ -38,10 +38,14 @@ | |||
605 | 38 | from zope.interface.verify import verifyObject | 38 | from zope.interface.verify import verifyObject |
606 | 39 | 39 | ||
607 | 40 | from mailman.config import config | 40 | from mailman.config import config |
608 | 41 | from mailman.database.alembic import alembic_cfg | ||
609 | 41 | from mailman.database.model import Model | 42 | from mailman.database.model import Model |
613 | 42 | from mailman.database.alembic import alembic_cfg | 43 | from mailman.interfaces.database import ( |
614 | 43 | from mailman.interfaces.database import IDatabase, IDatabaseFactory | 44 | DatabaseError, IDatabase, IDatabaseFactory) |
615 | 44 | from mailman.utilities.modules import call_name, expand_path | 45 | from mailman.utilities.modules import call_name |
616 | 46 | |||
617 | 47 | |||
618 | 48 | LAST_STORM_SCHEMA_VERSION = '20130406000000' | ||
619 | 45 | 49 | ||
620 | 46 | 50 | ||
621 | 47 | 51 | ||
622 | 48 | 52 | ||
623 | @@ -57,67 +61,58 @@ | |||
624 | 57 | database = call_name(database_class) | 61 | database = call_name(database_class) |
625 | 58 | verifyObject(IDatabase, database) | 62 | verifyObject(IDatabase, database) |
626 | 59 | database.initialize() | 63 | database.initialize() |
629 | 60 | schema_mgr = SchemaManager(database) | 64 | SchemaManager(database).setup_database() |
628 | 61 | schema_mgr.setup_db() | ||
630 | 62 | database.commit() | 65 | database.commit() |
631 | 63 | return database | 66 | return database |
632 | 64 | 67 | ||
633 | 65 | 68 | ||
634 | 66 | 69 | ||
635 | 67 | 70 | ||
636 | 68 | class SchemaManager: | 71 | class SchemaManager: |
639 | 69 | 72 | "Manage schema migrations.""" | |
638 | 70 | LAST_STORM_SCHEMA_VERSION = '20130406000000' | ||
640 | 71 | 73 | ||
641 | 72 | def __init__(self, database): | 74 | def __init__(self, database): |
644 | 73 | self.database = database | 75 | self._database = database |
645 | 74 | self.script = ScriptDirectory.from_config(alembic_cfg) | 76 | self._script = ScriptDirectory.from_config(alembic_cfg) |
646 | 75 | 77 | ||
651 | 76 | def get_storm_schema_version(self): | 78 | def _get_storm_schema_version(self): |
652 | 77 | md = MetaData() | 79 | metadata = MetaData() |
653 | 78 | md.reflect(bind=self.database.engine) | 80 | metadata.reflect(bind=self._database.engine) |
654 | 79 | if "version" not in md.tables: | 81 | if 'version' not in metadata.tables: |
655 | 82 | # There are no Storm artifacts left. | ||
656 | 80 | return None | 83 | return None |
663 | 81 | Version = md.tables["version"] | 84 | Version = metadata.tables['version'] |
664 | 82 | last_version = self.database.store.query(Version.c.version).filter( | 85 | last_version = self._database.store.query(Version.c.version).filter( |
665 | 83 | Version.c.component == "schema" | 86 | Version.c.component == 'schema' |
666 | 84 | ).order_by(Version.c.version.desc()).first() | 87 | ).order_by(Version.c.version.desc()).first() |
667 | 85 | # Don't leave open transactions or they will block any schema change | 88 | # Don't leave open transactions or they will block any schema change. |
668 | 86 | self.database.commit() | 89 | self._database.commit() |
669 | 87 | return last_version | 90 | return last_version |
670 | 88 | 91 | ||
682 | 89 | def _create(self): | 92 | def setup_database(self): |
683 | 90 | # initial DB creation | 93 | context = MigrationContext.configure(self._database.store.connection()) |
673 | 91 | Model.metadata.create_all(self.database.engine) | ||
674 | 92 | self.database.commit() | ||
675 | 93 | command.stamp(alembic_cfg, "head") | ||
676 | 94 | |||
677 | 95 | def _upgrade(self): | ||
678 | 96 | command.upgrade(alembic_cfg, "head") | ||
679 | 97 | |||
680 | 98 | def setup_db(self): | ||
681 | 99 | context = MigrationContext.configure(self.database.store.connection()) | ||
684 | 100 | current_rev = context.get_current_revision() | 94 | current_rev = context.get_current_revision() |
686 | 101 | head_rev = self.script.get_current_head() | 95 | head_rev = self._script.get_current_head() |
687 | 102 | if current_rev == head_rev: | 96 | if current_rev == head_rev: |
692 | 103 | return head_rev # already at the latest revision, nothing to do | 97 | # We're already at the latest revision so there's nothing to do. |
693 | 104 | if current_rev == None: | 98 | return head_rev |
694 | 105 | # no alembic information | 99 | if current_rev is None: |
695 | 106 | storm_version = self.get_storm_schema_version() | 100 | # No Alembic information is available. |
696 | 101 | storm_version = self._get_storm_schema_version() | ||
697 | 107 | if storm_version is None: | 102 | if storm_version is None: |
700 | 108 | # initial DB creation | 103 | # Initial database creation. |
701 | 109 | self._create() | 104 | Model.metadata.create_all(self._database.engine) |
702 | 105 | self._database.commit() | ||
703 | 106 | alembic.command.stamp(alembic_cfg, 'head') | ||
704 | 110 | else: | 107 | else: |
714 | 111 | # DB from a previous version managed by Storm | 108 | # The database was previously managed by Storm. |
715 | 112 | if storm_version.version < self.LAST_STORM_SCHEMA_VERSION: | 109 | if storm_version.version < LAST_STORM_SCHEMA_VERSION: |
716 | 113 | raise RuntimeError( | 110 | raise DatabaseError( |
717 | 114 | "Upgrading while skipping beta version is " | 111 | 'Upgrades skipping beta versions is not supported.') |
718 | 115 | "unsupported, please install the previous " | 112 | # Run migrations to remove the Storm-specific table and upgrade |
719 | 116 | "Mailman beta release") | 113 | # to SQLAlchemy and Alembic. |
720 | 117 | # Run migrations to remove the Storm-specific table and | 114 | alembic.command.upgrade(alembic_cfg, 'head') |
712 | 118 | # upgrade to SQLAlchemy & Alembic | ||
713 | 119 | self._upgrade() | ||
721 | 120 | elif current_rev != head_rev: | 115 | elif current_rev != head_rev: |
723 | 121 | self._upgrade() | 116 | alembic.command.upgrade(alembic_cfg, 'head') |
724 | 122 | return head_rev | 117 | return head_rev |
725 | 123 | 118 | ||
726 | 124 | 119 | ||
727 | 125 | 120 | ||
728 | === modified file 'src/mailman/database/tests/test_factory.py' | |||
729 | --- src/mailman/database/tests/test_factory.py 2014-10-10 16:58:48 +0000 | |||
730 | +++ src/mailman/database/tests/test_factory.py 2014-10-31 13:23:25 +0000 | |||
731 | @@ -21,24 +21,24 @@ | |||
732 | 21 | 21 | ||
733 | 22 | __metaclass__ = type | 22 | __metaclass__ = type |
734 | 23 | __all__ = [ | 23 | __all__ = [ |
735 | 24 | 'TestSchemaManager', | ||
736 | 24 | ] | 25 | ] |
737 | 25 | 26 | ||
738 | 26 | 27 | ||
739 | 27 | import unittest | 28 | import unittest |
740 | 28 | import types | ||
741 | 29 | |||
742 | 30 | import alembic.command | 29 | import alembic.command |
744 | 31 | from mock import Mock | 30 | |
745 | 31 | from mock import patch | ||
746 | 32 | from sqlalchemy import MetaData, Table, Column, Integer, Unicode | 32 | from sqlalchemy import MetaData, Table, Column, Integer, Unicode |
747 | 33 | from sqlalchemy.exc import ProgrammingError, OperationalError | ||
748 | 33 | from sqlalchemy.schema import Index | 34 | from sqlalchemy.schema import Index |
749 | 34 | from sqlalchemy.exc import ProgrammingError, OperationalError | ||
750 | 35 | 35 | ||
751 | 36 | from mailman.config import config | 36 | from mailman.config import config |
752 | 37 | from mailman.testing.layers import ConfigLayer | ||
753 | 38 | from mailman.database.factory import SchemaManager, _reset | ||
754 | 39 | from mailman.database.sqlite import SQLiteDatabase | ||
755 | 40 | from mailman.database.alembic import alembic_cfg | 37 | from mailman.database.alembic import alembic_cfg |
756 | 38 | from mailman.database.factory import LAST_STORM_SCHEMA_VERSION, SchemaManager | ||
757 | 41 | from mailman.database.model import Model | 39 | from mailman.database.model import Model |
758 | 40 | from mailman.interfaces.database import DatabaseError | ||
759 | 41 | from mailman.testing.layers import ConfigLayer | ||
760 | 42 | 42 | ||
761 | 43 | 43 | ||
762 | 44 | 44 | ||
763 | 45 | 45 | ||
764 | @@ -47,116 +47,114 @@ | |||
765 | 47 | layer = ConfigLayer | 47 | layer = ConfigLayer |
766 | 48 | 48 | ||
767 | 49 | def setUp(self): | 49 | def setUp(self): |
769 | 50 | # Drop the existing database | 50 | # Drop the existing database. |
770 | 51 | Model.metadata.drop_all(config.db.engine) | 51 | Model.metadata.drop_all(config.db.engine) |
771 | 52 | md = MetaData() | 52 | md = MetaData() |
772 | 53 | md.reflect(bind=config.db.engine) | 53 | md.reflect(bind=config.db.engine) |
774 | 54 | for tablename in ("alembic_version", "version"): | 54 | for tablename in ('alembic_version', 'version'): |
775 | 55 | if tablename in md.tables: | 55 | if tablename in md.tables: |
776 | 56 | md.tables[tablename].drop(config.db.engine) | 56 | md.tables[tablename].drop(config.db.engine) |
777 | 57 | self.schema_mgr = SchemaManager(config.db) | 57 | self.schema_mgr = SchemaManager(config.db) |
778 | 58 | 58 | ||
779 | 59 | def tearDown(self): | 59 | def tearDown(self): |
780 | 60 | self._drop_storm_database() | 60 | self._drop_storm_database() |
782 | 61 | # Restore a virgin DB | 61 | # Restore a virgin database. |
783 | 62 | Model.metadata.create_all(config.db.engine) | 62 | Model.metadata.create_all(config.db.engine) |
784 | 63 | 63 | ||
785 | 64 | |||
786 | 65 | def _table_exists(self, tablename): | 64 | def _table_exists(self, tablename): |
787 | 66 | md = MetaData() | 65 | md = MetaData() |
788 | 67 | md.reflect(bind=config.db.engine) | 66 | md.reflect(bind=config.db.engine) |
789 | 68 | return tablename in md.tables | 67 | return tablename in md.tables |
790 | 69 | 68 | ||
791 | 70 | def _create_storm_database(self, revision): | 69 | def _create_storm_database(self, revision): |
797 | 71 | version_table = Table("version", Model.metadata, | 70 | version_table = Table( |
798 | 72 | Column("id", Integer, primary_key=True), | 71 | 'version', Model.metadata, |
799 | 73 | Column("component", Unicode), | 72 | Column('id', Integer, primary_key=True), |
800 | 74 | Column("version", Unicode), | 73 | Column('component', Unicode), |
801 | 75 | ) | 74 | Column('version', Unicode), |
802 | 75 | ) | ||
803 | 76 | version_table.create(config.db.engine) | 76 | version_table.create(config.db.engine) |
804 | 77 | config.db.store.execute(version_table.insert().values( | 77 | config.db.store.execute(version_table.insert().values( |
806 | 78 | component='schema', version=revision)) | 78 | component='schema', version=revision)) |
807 | 79 | config.db.commit() | 79 | config.db.commit() |
808 | 80 | # Other Storm specific changes, those SQL statements hopefully work on | 80 | # Other Storm specific changes, those SQL statements hopefully work on |
809 | 81 | # all DB engines... | 81 | # all DB engines... |
810 | 82 | config.db.engine.execute( | 82 | config.db.engine.execute( |
814 | 83 | "ALTER TABLE mailinglist ADD COLUMN acceptable_aliases_id INT") | 83 | 'ALTER TABLE mailinglist ADD COLUMN acceptable_aliases_id INT') |
815 | 84 | Index("ix_user__user_id").drop(bind=config.db.engine) | 84 | Index('ix_user__user_id').drop(bind=config.db.engine) |
816 | 85 | # Don't pollute our main metadata object, create a new one | 85 | # Don't pollute our main metadata object, create a new one. |
817 | 86 | md = MetaData() | 86 | md = MetaData() |
821 | 87 | user_table = Model.metadata.tables["user"].tometadata(md) | 87 | user_table = Model.metadata.tables['user'].tometadata(md) |
822 | 88 | Index("ix_user_user_id", user_table.c._user_id | 88 | Index('ix_user_user_id', user_table.c._user_id).create( |
823 | 89 | ).create(bind=config.db.engine) | 89 | bind=config.db.engine) |
824 | 90 | config.db.commit() | 90 | config.db.commit() |
825 | 91 | 91 | ||
826 | 92 | def _drop_storm_database(self): | 92 | def _drop_storm_database(self): |
833 | 93 | """ | 93 | """Remove the leftovers from a Storm DB. |
834 | 94 | Remove the leftovers from a Storm DB. | 94 | |
835 | 95 | (you must issue a drop_all() afterwards) | 95 | A drop_all() must be issued afterwards. |
836 | 96 | """ | 96 | """ |
837 | 97 | if "version" in Model.metadata.tables: | 97 | if 'version' in Model.metadata.tables: |
838 | 98 | version = Model.metadata.tables["version"] | 98 | version = Model.metadata.tables['version'] |
839 | 99 | version.drop(config.db.engine, checkfirst=True) | 99 | version.drop(config.db.engine, checkfirst=True) |
840 | 100 | Model.metadata.remove(version) | 100 | Model.metadata.remove(version) |
841 | 101 | try: | 101 | try: |
846 | 102 | Index("ix_user_user_id").drop(bind=config.db.engine) | 102 | Index('ix_user_user_id').drop(bind=config.db.engine) |
847 | 103 | except (ProgrammingError, OperationalError) as e: | 103 | except (ProgrammingError, OperationalError): |
848 | 104 | # non-existant (PGSQL raises a ProgrammingError, while SQLite | 104 | # Nonexistent. PostgreSQL raises a ProgrammingError, while SQLite |
849 | 105 | # raises an OperationalError) | 105 | # raises an OperationalError. |
850 | 106 | pass | 106 | pass |
851 | 107 | config.db.commit() | 107 | config.db.commit() |
852 | 108 | 108 | ||
892 | 109 | 109 | def test_current_database(self): | |
893 | 110 | def test_current_db(self): | 110 | # The database is already at the latest version. |
894 | 111 | """The database is already at the latest version""" | 111 | alembic.command.stamp(alembic_cfg, 'head') |
895 | 112 | alembic.command.stamp(alembic_cfg, "head") | 112 | with patch('alembic.command') as alembic_command: |
896 | 113 | self.schema_mgr._create = Mock() | 113 | self.schema_mgr.setup_database() |
897 | 114 | self.schema_mgr._upgrade = Mock() | 114 | self.assertFalse(alembic_command.stamp.called) |
898 | 115 | self.schema_mgr.setup_db() | 115 | self.assertFalse(alembic_command.upgrade.called) |
899 | 116 | self.assertFalse(self.schema_mgr._create.called) | 116 | |
900 | 117 | self.assertFalse(self.schema_mgr._upgrade.called) | 117 | @patch('alembic.command') |
901 | 118 | 118 | def test_initial(self, alembic_command): | |
902 | 119 | def test_initial(self): | 119 | # No existing database. |
903 | 120 | """No existing database""" | 120 | self.assertFalse(self._table_exists('mailinglist')) |
904 | 121 | self.assertFalse(self._table_exists("mailinglist")) | 121 | self.assertFalse(self._table_exists('alembic_version')) |
905 | 122 | self.assertFalse(self._table_exists("alembic_version")) | 122 | self.schema_mgr.setup_database() |
906 | 123 | self.schema_mgr._upgrade = Mock() | 123 | self.assertFalse(alembic_command.upgrade.called) |
907 | 124 | self.schema_mgr.setup_db() | 124 | self.assertTrue(self._table_exists('mailinglist')) |
908 | 125 | self.assertFalse(self.schema_mgr._upgrade.called) | 125 | self.assertTrue(self._table_exists('alembic_version')) |
909 | 126 | self.assertTrue(self._table_exists("mailinglist")) | 126 | |
910 | 127 | self.assertTrue(self._table_exists("alembic_version")) | 127 | @patch('alembic.command.stamp') |
911 | 128 | 128 | def test_storm(self, alembic_command_stamp): | |
912 | 129 | def test_storm(self): | 129 | # Existing Storm database. |
913 | 130 | """Existing Storm database""" | 130 | Model.metadata.create_all(config.db.engine) |
914 | 131 | Model.metadata.create_all(config.db.engine) | 131 | self._create_storm_database(LAST_STORM_SCHEMA_VERSION) |
915 | 132 | self._create_storm_database( | 132 | self.schema_mgr.setup_database() |
916 | 133 | self.schema_mgr.LAST_STORM_SCHEMA_VERSION) | 133 | self.assertFalse(alembic_command_stamp.called) |
917 | 134 | self.schema_mgr._create = Mock() | 134 | self.assertTrue( |
918 | 135 | self.schema_mgr.setup_db() | 135 | self._table_exists('mailinglist') |
919 | 136 | self.assertFalse(self.schema_mgr._create.called) | 136 | and self._table_exists('alembic_version') |
920 | 137 | self.assertTrue(self._table_exists("mailinglist") | 137 | and not self._table_exists('version')) |
921 | 138 | and self._table_exists("alembic_version") | 138 | |
922 | 139 | and not self._table_exists("version")) | 139 | @patch('alembic.command') |
923 | 140 | 140 | def test_old_storm(self, alembic_command): | |
924 | 141 | def test_old_storm(self): | 141 | # Existing Storm database in an old version. |
925 | 142 | """Existing Storm database in an old version""" | 142 | Model.metadata.create_all(config.db.engine) |
926 | 143 | Model.metadata.create_all(config.db.engine) | 143 | self._create_storm_database('001') |
927 | 144 | self._create_storm_database("001") | 144 | self.assertRaises(DatabaseError, self.schema_mgr.setup_database) |
928 | 145 | self.schema_mgr._create = Mock() | 145 | self.assertFalse(alembic_command.stamp.called) |
929 | 146 | self.assertRaises(RuntimeError, self.schema_mgr.setup_db) | 146 | self.assertFalse(alembic_command.upgrade.called) |
891 | 147 | self.assertFalse(self.schema_mgr._create.called) | ||
930 | 148 | 147 | ||
931 | 149 | def test_old_db(self): | 148 | def test_old_db(self): |
934 | 150 | """The database is in an old revision, must upgrade""" | 149 | # The database is in an old revision, must upgrade. |
935 | 151 | alembic.command.stamp(alembic_cfg, "head") | 150 | alembic.command.stamp(alembic_cfg, 'head') |
936 | 152 | md = MetaData() | 151 | md = MetaData() |
937 | 153 | md.reflect(bind=config.db.engine) | 152 | md.reflect(bind=config.db.engine) |
941 | 154 | config.db.store.execute(md.tables["alembic_version"].delete()) | 153 | config.db.store.execute(md.tables['alembic_version'].delete()) |
942 | 155 | config.db.store.execute(md.tables["alembic_version"].insert().values( | 154 | config.db.store.execute(md.tables['alembic_version'].insert().values( |
943 | 156 | version_num="dummyrevision")) | 155 | version_num='dummyrevision')) |
944 | 157 | config.db.commit() | 156 | config.db.commit() |
950 | 158 | self.schema_mgr._create = Mock() | 157 | with patch('alembic.command') as alembic_command: |
951 | 159 | self.schema_mgr._upgrade = Mock() | 158 | self.schema_mgr.setup_database() |
952 | 160 | self.schema_mgr.setup_db() | 159 | self.assertFalse(alembic_command.stamp.called) |
953 | 161 | self.assertFalse(self.schema_mgr._create.called) | 160 | self.assertTrue(alembic_command.upgrade.called) |
949 | 162 | self.assertTrue(self.schema_mgr._upgrade.called) | ||
954 | 163 | 161 | ||
955 | === modified file 'src/mailman/handlers/docs/owner-recips.rst' | |||
956 | --- src/mailman/handlers/docs/owner-recips.rst 2012-03-23 20:34:54 +0000 | |||
957 | +++ src/mailman/handlers/docs/owner-recips.rst 2014-10-31 13:23:25 +0000 | |||
958 | @@ -41,7 +41,7 @@ | |||
959 | 41 | >>> handler.process(mlist_1, msg, msgdata) | 41 | >>> handler.process(mlist_1, msg, msgdata) |
960 | 42 | >>> dump_list(msgdata['recipients']) | 42 | >>> dump_list(msgdata['recipients']) |
961 | 43 | bart@example.com | 43 | bart@example.com |
963 | 44 | 44 | ||
964 | 45 | If Bart also disables his owner delivery, then no one could contact the list's | 45 | If Bart also disables his owner delivery, then no one could contact the list's |
965 | 46 | owners. Since this is unacceptable, the site owner is used as a fallback. | 46 | owners. Since this is unacceptable, the site owner is used as a fallback. |
966 | 47 | 47 | ||
967 | @@ -55,7 +55,7 @@ | |||
968 | 55 | a fallback. | 55 | a fallback. |
969 | 56 | 56 | ||
970 | 57 | >>> mlist_2 = create_list('beta@example.com') | 57 | >>> mlist_2 = create_list('beta@example.com') |
972 | 58 | >>> mlist_2.administrators.member_count | 58 | >>> print(mlist_2.administrators.member_count) |
973 | 59 | 0 | 59 | 0 |
974 | 60 | >>> msgdata = {} | 60 | >>> msgdata = {} |
975 | 61 | >>> handler.process(mlist_2, msg, msgdata) | 61 | >>> handler.process(mlist_2, msg, msgdata) |
976 | 62 | 62 | ||
977 | === modified file 'src/mailman/model/docs/autorespond.rst' | |||
978 | --- src/mailman/model/docs/autorespond.rst 2014-04-28 15:23:35 +0000 | |||
979 | +++ src/mailman/model/docs/autorespond.rst 2014-10-31 13:23:25 +0000 | |||
980 | @@ -37,34 +37,34 @@ | |||
981 | 37 | ... 'aperson@example.com') | 37 | ... 'aperson@example.com') |
982 | 38 | 38 | ||
983 | 39 | >>> from mailman.interfaces.autorespond import Response | 39 | >>> from mailman.interfaces.autorespond import Response |
985 | 40 | >>> response_set.todays_count(address, Response.hold) | 40 | >>> print(response_set.todays_count(address, Response.hold)) |
986 | 41 | 0 | 41 | 0 |
988 | 42 | >>> response_set.todays_count(address, Response.command) | 42 | >>> print(response_set.todays_count(address, Response.command)) |
989 | 43 | 0 | 43 | 0 |
990 | 44 | 44 | ||
991 | 45 | Using the response set, we can record that a hold response is sent to the | 45 | Using the response set, we can record that a hold response is sent to the |
992 | 46 | address. | 46 | address. |
993 | 47 | 47 | ||
994 | 48 | >>> response_set.response_sent(address, Response.hold) | 48 | >>> response_set.response_sent(address, Response.hold) |
996 | 49 | >>> response_set.todays_count(address, Response.hold) | 49 | >>> print(response_set.todays_count(address, Response.hold)) |
997 | 50 | 1 | 50 | 1 |
999 | 51 | >>> response_set.todays_count(address, Response.command) | 51 | >>> print(response_set.todays_count(address, Response.command)) |
1000 | 52 | 0 | 52 | 0 |
1001 | 53 | 53 | ||
1002 | 54 | We can also record that a command response was sent. | 54 | We can also record that a command response was sent. |
1003 | 55 | 55 | ||
1004 | 56 | >>> response_set.response_sent(address, Response.command) | 56 | >>> response_set.response_sent(address, Response.command) |
1006 | 57 | >>> response_set.todays_count(address, Response.hold) | 57 | >>> print(response_set.todays_count(address, Response.hold)) |
1007 | 58 | 1 | 58 | 1 |
1009 | 59 | >>> response_set.todays_count(address, Response.command) | 59 | >>> print(response_set.todays_count(address, Response.command)) |
1010 | 60 | 1 | 60 | 1 |
1011 | 61 | 61 | ||
1012 | 62 | Let's send one more. | 62 | Let's send one more. |
1013 | 63 | 63 | ||
1014 | 64 | >>> response_set.response_sent(address, Response.command) | 64 | >>> response_set.response_sent(address, Response.command) |
1016 | 65 | >>> response_set.todays_count(address, Response.hold) | 65 | >>> print(response_set.todays_count(address, Response.hold)) |
1017 | 66 | 1 | 66 | 1 |
1019 | 67 | >>> response_set.todays_count(address, Response.command) | 67 | >>> print(response_set.todays_count(address, Response.command)) |
1020 | 68 | 2 | 68 | 2 |
1021 | 69 | 69 | ||
1022 | 70 | Now the day flips over and all the counts reset. | 70 | Now the day flips over and all the counts reset. |
1023 | @@ -73,9 +73,9 @@ | |||
1024 | 73 | >>> from mailman.utilities.datetime import factory | 73 | >>> from mailman.utilities.datetime import factory |
1025 | 74 | >>> factory.fast_forward() | 74 | >>> factory.fast_forward() |
1026 | 75 | 75 | ||
1028 | 76 | >>> response_set.todays_count(address, Response.hold) | 76 | >>> print(response_set.todays_count(address, Response.hold)) |
1029 | 77 | 0 | 77 | 0 |
1031 | 78 | >>> response_set.todays_count(address, Response.command) | 78 | >>> print(response_set.todays_count(address, Response.command)) |
1032 | 79 | 0 | 79 | 0 |
1033 | 80 | 80 | ||
1034 | 81 | 81 | ||
1035 | @@ -110,7 +110,7 @@ | |||
1036 | 110 | 110 | ||
1037 | 111 | >>> address = getUtility(IUserManager).create_address( | 111 | >>> address = getUtility(IUserManager).create_address( |
1038 | 112 | ... 'bperson@example.com') | 112 | ... 'bperson@example.com') |
1040 | 113 | >>> response_set.todays_count(address, Response.command) | 113 | >>> print(response_set.todays_count(address, Response.command)) |
1041 | 114 | 0 | 114 | 0 |
1042 | 115 | >>> print(response_set.last_response(address, Response.command)) | 115 | >>> print(response_set.last_response(address, Response.command)) |
1043 | 116 | None | 116 | None |
1044 | 117 | 117 | ||
1045 | === modified file 'src/mailman/model/docs/mailinglist.rst' | |||
1046 | --- src/mailman/model/docs/mailinglist.rst 2014-04-28 15:23:35 +0000 | |||
1047 | +++ src/mailman/model/docs/mailinglist.rst 2014-10-31 13:23:25 +0000 | |||
1048 | @@ -50,7 +50,7 @@ | |||
1049 | 50 | 50 | ||
1050 | 51 | Both addresses appear on the roster of members. | 51 | Both addresses appear on the roster of members. |
1051 | 52 | 52 | ||
1053 | 53 | >>> for member in mlist.members.members: | 53 | >>> for member in sorted(mlist.members.members, key=lambda m: m.address.email): |
1054 | 54 | ... print(member) | 54 | ... print(member) |
1055 | 55 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> | 55 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> |
1056 | 56 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> | 56 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> |
1057 | @@ -72,7 +72,7 @@ | |||
1058 | 72 | an owner and a moderator. | 72 | an owner and a moderator. |
1059 | 73 | :: | 73 | :: |
1060 | 74 | 74 | ||
1062 | 75 | >>> for member in mlist.owners.members: | 75 | >>> for member in sorted(mlist.owners.members, key=lambda m: m.address.email): |
1063 | 76 | ... print(member) | 76 | ... print(member) |
1064 | 77 | <Member: aperson@example.com on aardvark@example.com as MemberRole.owner> | 77 | <Member: aperson@example.com on aardvark@example.com as MemberRole.owner> |
1065 | 78 | <Member: cperson@example.com on aardvark@example.com as MemberRole.owner> | 78 | <Member: cperson@example.com on aardvark@example.com as MemberRole.owner> |
1066 | @@ -87,13 +87,13 @@ | |||
1067 | 87 | :: | 87 | :: |
1068 | 88 | 88 | ||
1069 | 89 | >>> roster = mlist.get_roster(MemberRole.member) | 89 | >>> roster = mlist.get_roster(MemberRole.member) |
1071 | 90 | >>> for member in roster.members: | 90 | >>> for member in sorted(roster.members, key=lambda m: m.address.email): |
1072 | 91 | ... print(member) | 91 | ... print(member) |
1073 | 92 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> | 92 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> |
1074 | 93 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> | 93 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> |
1075 | 94 | 94 | ||
1076 | 95 | >>> roster = mlist.get_roster(MemberRole.owner) | 95 | >>> roster = mlist.get_roster(MemberRole.owner) |
1078 | 96 | >>> for member in roster.members: | 96 | >>> for member in sorted(roster.members, key=lambda m: m.address.email): |
1079 | 97 | ... print(member) | 97 | ... print(member) |
1080 | 98 | <Member: aperson@example.com on aardvark@example.com as MemberRole.owner> | 98 | <Member: aperson@example.com on aardvark@example.com as MemberRole.owner> |
1081 | 99 | <Member: cperson@example.com on aardvark@example.com as MemberRole.owner> | 99 | <Member: cperson@example.com on aardvark@example.com as MemberRole.owner> |
1082 | @@ -122,7 +122,7 @@ | |||
1083 | 122 | >>> mlist.subscribe(user) | 122 | >>> mlist.subscribe(user) |
1084 | 123 | <Member: Dave Person <dperson@example.com> on aardvark@example.com | 123 | <Member: Dave Person <dperson@example.com> on aardvark@example.com |
1085 | 124 | as MemberRole.member> | 124 | as MemberRole.member> |
1087 | 125 | >>> for member in mlist.members.members: | 125 | >>> for member in sorted(mlist.members.members, key=lambda m: m.address.email): |
1088 | 126 | ... print(member) | 126 | ... print(member) |
1089 | 127 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> | 127 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> |
1090 | 128 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> | 128 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> |
1091 | @@ -133,7 +133,7 @@ | |||
1092 | 133 | >>> new_address.verified_on = now() | 133 | >>> new_address.verified_on = now() |
1093 | 134 | >>> user.preferred_address = new_address | 134 | >>> user.preferred_address = new_address |
1094 | 135 | 135 | ||
1096 | 136 | >>> for member in mlist.members.members: | 136 | >>> for member in sorted(mlist.members.members, key=lambda m: m.address.email): |
1097 | 137 | ... print(member) | 137 | ... print(member) |
1098 | 138 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> | 138 | <Member: aperson@example.com on aardvark@example.com as MemberRole.member> |
1099 | 139 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> | 139 | <Member: bperson@example.com on aardvark@example.com as MemberRole.member> |
1100 | 140 | 140 | ||
1101 | === modified file 'src/mailman/model/docs/requests.rst' | |||
1102 | --- src/mailman/model/docs/requests.rst 2014-04-28 15:23:35 +0000 | |||
1103 | +++ src/mailman/model/docs/requests.rst 2014-10-31 13:23:25 +0000 | |||
1104 | @@ -35,7 +35,7 @@ | |||
1105 | 35 | 35 | ||
1106 | 36 | The list's requests database starts out empty. | 36 | The list's requests database starts out empty. |
1107 | 37 | 37 | ||
1109 | 38 | >>> requests.count | 38 | >>> print(requests.count) |
1110 | 39 | 0 | 39 | 0 |
1111 | 40 | >>> dump_list(requests.held_requests) | 40 | >>> dump_list(requests.held_requests) |
1112 | 41 | *Empty* | 41 | *Empty* |
1113 | @@ -68,21 +68,21 @@ | |||
1114 | 68 | 68 | ||
1115 | 69 | We can see the total number of requests being held. | 69 | We can see the total number of requests being held. |
1116 | 70 | 70 | ||
1118 | 71 | >>> requests.count | 71 | >>> print(requests.count) |
1119 | 72 | 3 | 72 | 3 |
1120 | 73 | 73 | ||
1121 | 74 | We can also see the number of requests being held by request type. | 74 | We can also see the number of requests being held by request type. |
1122 | 75 | 75 | ||
1124 | 76 | >>> requests.count_of(RequestType.subscription) | 76 | >>> print(requests.count_of(RequestType.subscription)) |
1125 | 77 | 1 | 77 | 1 |
1127 | 78 | >>> requests.count_of(RequestType.unsubscription) | 78 | >>> print(requests.count_of(RequestType.unsubscription)) |
1128 | 79 | 1 | 79 | 1 |
1129 | 80 | 80 | ||
1130 | 81 | We can also see when there are multiple held requests of a particular type. | 81 | We can also see when there are multiple held requests of a particular type. |
1131 | 82 | 82 | ||
1133 | 83 | >>> requests.hold_request(RequestType.held_message, 'hold_4') | 83 | >>> print(requests.hold_request(RequestType.held_message, 'hold_4')) |
1134 | 84 | 4 | 84 | 4 |
1136 | 85 | >>> requests.count_of(RequestType.held_message) | 85 | >>> print(requests.count_of(RequestType.held_message)) |
1137 | 86 | 2 | 86 | 2 |
1138 | 87 | 87 | ||
1139 | 88 | We can ask the requests database for a specific request, by providing the id | 88 | We can ask the requests database for a specific request, by providing the id |
1140 | @@ -132,7 +132,7 @@ | |||
1141 | 132 | To make it easier to find specific requests, the list requests can be iterated | 132 | To make it easier to find specific requests, the list requests can be iterated |
1142 | 133 | over by type. | 133 | over by type. |
1143 | 134 | 134 | ||
1145 | 135 | >>> requests.count_of(RequestType.held_message) | 135 | >>> print(requests.count_of(RequestType.held_message)) |
1146 | 136 | 3 | 136 | 3 |
1147 | 137 | >>> for request in requests.of_type(RequestType.held_message): | 137 | >>> for request in requests.of_type(RequestType.held_message): |
1148 | 138 | ... key, data = requests.get_request(request.id) | 138 | ... key, data = requests.get_request(request.id) |
1149 | @@ -154,10 +154,10 @@ | |||
1150 | 154 | Once a specific request has been handled, it can be deleted from the requests | 154 | Once a specific request has been handled, it can be deleted from the requests |
1151 | 155 | database. | 155 | database. |
1152 | 156 | 156 | ||
1154 | 157 | >>> requests.count | 157 | >>> print(requests.count) |
1155 | 158 | 5 | 158 | 5 |
1156 | 159 | >>> requests.delete_request(2) | 159 | >>> requests.delete_request(2) |
1158 | 160 | >>> requests.count | 160 | >>> print(requests.count) |
1159 | 161 | 4 | 161 | 4 |
1160 | 162 | 162 | ||
1161 | 163 | Request 2 is no longer in the database. | 163 | Request 2 is no longer in the database. |
1162 | @@ -167,5 +167,5 @@ | |||
1163 | 167 | 167 | ||
1164 | 168 | >>> for request in requests.held_requests: | 168 | >>> for request in requests.held_requests: |
1165 | 169 | ... requests.delete_request(request.id) | 169 | ... requests.delete_request(request.id) |
1167 | 170 | >>> requests.count | 170 | >>> print(requests.count) |
1168 | 171 | 0 | 171 | 0 |
1169 | 172 | 172 | ||
1170 | === modified file 'src/mailman/model/domain.py' | |||
1171 | --- src/mailman/model/domain.py 2014-09-22 18:47:02 +0000 | |||
1172 | +++ src/mailman/model/domain.py 2014-10-31 13:23:25 +0000 | |||
1173 | @@ -48,7 +48,7 @@ | |||
1174 | 48 | 48 | ||
1175 | 49 | id = Column(Integer, primary_key=True) | 49 | id = Column(Integer, primary_key=True) |
1176 | 50 | 50 | ||
1178 | 51 | mail_host = Column(Unicode) | 51 | mail_host = Column(Unicode) # TODO: add index? |
1179 | 52 | base_url = Column(Unicode) | 52 | base_url = Column(Unicode) |
1180 | 53 | description = Column(Unicode) | 53 | description = Column(Unicode) |
1181 | 54 | contact_address = Column(Unicode) | 54 | contact_address = Column(Unicode) |
1182 | @@ -95,7 +95,8 @@ | |||
1183 | 95 | def mailing_lists(self, store): | 95 | def mailing_lists(self, store): |
1184 | 96 | """See `IDomain`.""" | 96 | """See `IDomain`.""" |
1185 | 97 | mailing_lists = store.query(MailingList).filter( | 97 | mailing_lists = store.query(MailingList).filter( |
1187 | 98 | MailingList.mail_host == self.mail_host) | 98 | MailingList.mail_host == self.mail_host |
1188 | 99 | ).order_by(MailingList._list_id) | ||
1189 | 99 | for mlist in mailing_lists: | 100 | for mlist in mailing_lists: |
1190 | 100 | yield mlist | 101 | yield mlist |
1191 | 101 | 102 | ||
1192 | @@ -170,7 +171,7 @@ | |||
1193 | 170 | @dbconnection | 171 | @dbconnection |
1194 | 171 | def __iter__(self, store): | 172 | def __iter__(self, store): |
1195 | 172 | """See `IDomainManager`.""" | 173 | """See `IDomainManager`.""" |
1197 | 173 | for domain in store.query(Domain).all(): | 174 | for domain in store.query(Domain).order_by(Domain.mail_host).all(): |
1198 | 174 | yield domain | 175 | yield domain |
1199 | 175 | 176 | ||
1200 | 176 | @dbconnection | 177 | @dbconnection |
1201 | 177 | 178 | ||
1202 | === modified file 'src/mailman/model/listmanager.py' | |||
1203 | --- src/mailman/model/listmanager.py 2014-09-21 21:06:40 +0000 | |||
1204 | +++ src/mailman/model/listmanager.py 2014-10-31 13:23:25 +0000 | |||
1205 | @@ -86,7 +86,8 @@ | |||
1206 | 86 | @dbconnection | 86 | @dbconnection |
1207 | 87 | def mailing_lists(self, store): | 87 | def mailing_lists(self, store): |
1208 | 88 | """See `IListManager`.""" | 88 | """See `IListManager`.""" |
1210 | 89 | for mlist in store.query(MailingList).all(): | 89 | for mlist in store.query(MailingList).order_by( |
1211 | 90 | MailingList._list_id).all(): | ||
1212 | 90 | yield mlist | 91 | yield mlist |
1213 | 91 | 92 | ||
1214 | 92 | @dbconnection | 93 | @dbconnection |
1215 | 93 | 94 | ||
1216 | === modified file 'src/mailman/model/mailinglist.py' | |||
1217 | --- src/mailman/model/mailinglist.py 2014-10-10 04:59:43 +0000 | |||
1218 | +++ src/mailman/model/mailinglist.py 2014-10-31 13:23:25 +0000 | |||
1219 | @@ -504,10 +504,9 @@ | |||
1220 | 504 | 504 | ||
1221 | 505 | id = Column(Integer, primary_key=True) | 505 | id = Column(Integer, primary_key=True) |
1222 | 506 | 506 | ||
1227 | 507 | mailing_list_id = Column(Integer, | 507 | mailing_list_id = Column( |
1228 | 508 | ForeignKey('mailinglist.id'), | 508 | Integer, ForeignKey('mailinglist.id'), |
1229 | 509 | index=True, | 509 | index=True, nullable=False) |
1226 | 510 | nullable=False) | ||
1230 | 511 | mailing_list = relationship('MailingList', backref='acceptable_alias') | 510 | mailing_list = relationship('MailingList', backref='acceptable_alias') |
1231 | 512 | alias = Column(Unicode, index=True, nullable=False) | 511 | alias = Column(Unicode, index=True, nullable=False) |
1232 | 513 | 512 | ||
1233 | @@ -561,9 +560,11 @@ | |||
1234 | 561 | 560 | ||
1235 | 562 | id = Column(Integer, primary_key=True) | 561 | id = Column(Integer, primary_key=True) |
1236 | 563 | 562 | ||
1239 | 564 | mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'), | 563 | mailing_list_id = Column( |
1240 | 565 | index=True, nullable=False) | 564 | Integer, ForeignKey('mailinglist.id'), |
1241 | 565 | index=True, nullable=False) | ||
1242 | 566 | mailing_list = relationship('MailingList') | 566 | mailing_list = relationship('MailingList') |
1243 | 567 | |||
1244 | 567 | name = Column(Unicode, nullable=False) | 568 | name = Column(Unicode, nullable=False) |
1245 | 568 | _is_enabled = Column(Boolean) | 569 | _is_enabled = Column(Boolean) |
1246 | 569 | 570 | ||
1247 | 570 | 571 | ||
1248 | === modified file 'src/mailman/model/user.py' | |||
1249 | --- src/mailman/model/user.py 2014-10-10 04:59:43 +0000 | |||
1250 | +++ src/mailman/model/user.py 2014-10-31 13:23:25 +0000 | |||
1251 | @@ -56,7 +56,7 @@ | |||
1252 | 56 | 56 | ||
1253 | 57 | id = Column(Integer, primary_key=True) | 57 | id = Column(Integer, primary_key=True) |
1254 | 58 | display_name = Column(Unicode) | 58 | display_name = Column(Unicode) |
1256 | 59 | _password = Column('password', LargeBinary) # TODO : was RawStr() | 59 | _password = Column('password', LargeBinary) |
1257 | 60 | _user_id = Column(UUID, index=True) | 60 | _user_id = Column(UUID, index=True) |
1258 | 61 | _created_on = Column(DateTime) | 61 | _created_on = Column(DateTime) |
1259 | 62 | 62 | ||
1260 | @@ -68,7 +68,7 @@ | |||
1261 | 68 | Integer, | 68 | Integer, |
1262 | 69 | ForeignKey('address.id', use_alter=True, | 69 | ForeignKey('address.id', use_alter=True, |
1263 | 70 | name='_preferred_address', | 70 | name='_preferred_address', |
1265 | 71 | ondelete="SET NULL")) | 71 | ondelete='SET NULL')) |
1266 | 72 | 72 | ||
1267 | 73 | _preferred_address = relationship( | 73 | _preferred_address = relationship( |
1268 | 74 | 'Address', primaryjoin=(_preferred_address_id==Address.id), | 74 | 'Address', primaryjoin=(_preferred_address_id==Address.id), |
1269 | 75 | 75 | ||
1270 | === modified file 'src/mailman/rest/docs/moderation.rst' | |||
1271 | --- src/mailman/rest/docs/moderation.rst 2014-04-28 15:23:35 +0000 | |||
1272 | +++ src/mailman/rest/docs/moderation.rst 2014-10-31 13:23:25 +0000 | |||
1273 | @@ -226,10 +226,9 @@ | |||
1274 | 226 | 226 | ||
1275 | 227 | >>> from mailman.app.moderator import hold_subscription | 227 | >>> from mailman.app.moderator import hold_subscription |
1276 | 228 | >>> from mailman.interfaces.member import DeliveryMode | 228 | >>> from mailman.interfaces.member import DeliveryMode |
1278 | 229 | >>> hold_subscription( | 229 | >>> sub_req_id = hold_subscription( |
1279 | 230 | ... ant, 'anne@example.com', 'Anne Person', | 230 | ... ant, 'anne@example.com', 'Anne Person', |
1280 | 231 | ... 'password', DeliveryMode.regular, 'en') | 231 | ... 'password', DeliveryMode.regular, 'en') |
1281 | 232 | 1 | ||
1282 | 233 | >>> transaction.commit() | 232 | >>> transaction.commit() |
1283 | 234 | 233 | ||
1284 | 235 | The subscription request is available from the mailing list. | 234 | The subscription request is available from the mailing list. |
1285 | @@ -242,7 +241,7 @@ | |||
1286 | 242 | http_etag: "..." | 241 | http_etag: "..." |
1287 | 243 | language: en | 242 | language: en |
1288 | 244 | password: password | 243 | password: password |
1290 | 245 | request_id: 1 | 244 | request_id: ... |
1291 | 246 | type: subscription | 245 | type: subscription |
1292 | 247 | when: 2005-08-01T07:49:23 | 246 | when: 2005-08-01T07:49:23 |
1293 | 248 | http_etag: "..." | 247 | http_etag: "..." |
1294 | @@ -259,8 +258,7 @@ | |||
1295 | 259 | >>> from mailman.app.moderator import hold_unsubscription | 258 | >>> from mailman.app.moderator import hold_unsubscription |
1296 | 260 | >>> bart = add_member(ant, 'bart@example.com', 'Bart Person', | 259 | >>> bart = add_member(ant, 'bart@example.com', 'Bart Person', |
1297 | 261 | ... 'password', DeliveryMode.regular, 'en') | 260 | ... 'password', DeliveryMode.regular, 'en') |
1300 | 262 | >>> hold_unsubscription(ant, 'bart@example.com') | 261 | >>> unsub_req_id = hold_unsubscription(ant, 'bart@example.com') |
1299 | 263 | 2 | ||
1301 | 264 | >>> transaction.commit() | 262 | >>> transaction.commit() |
1302 | 265 | 263 | ||
1303 | 266 | The unsubscription request is also available from the mailing list. | 264 | The unsubscription request is also available from the mailing list. |
1304 | @@ -273,13 +271,13 @@ | |||
1305 | 273 | http_etag: "..." | 271 | http_etag: "..." |
1306 | 274 | language: en | 272 | language: en |
1307 | 275 | password: password | 273 | password: password |
1309 | 276 | request_id: 1 | 274 | request_id: ... |
1310 | 277 | type: subscription | 275 | type: subscription |
1311 | 278 | when: 2005-08-01T07:49:23 | 276 | when: 2005-08-01T07:49:23 |
1312 | 279 | entry 1: | 277 | entry 1: |
1313 | 280 | address: bart@example.com | 278 | address: bart@example.com |
1314 | 281 | http_etag: "..." | 279 | http_etag: "..." |
1316 | 282 | request_id: 2 | 280 | request_id: ... |
1317 | 283 | type: unsubscription | 281 | type: unsubscription |
1318 | 284 | http_etag: "..." | 282 | http_etag: "..." |
1319 | 285 | start: 0 | 283 | start: 0 |
1320 | @@ -292,23 +290,25 @@ | |||
1321 | 292 | You can view an individual membership change request by providing the | 290 | You can view an individual membership change request by providing the |
1322 | 293 | request id. Anne's subscription request looks like this. | 291 | request id. Anne's subscription request looks like this. |
1323 | 294 | 292 | ||
1325 | 295 | >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests/1') | 293 | >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' |
1326 | 294 | ... 'requests/{}'.format(sub_req_id)) | ||
1327 | 296 | address: anne@example.com | 295 | address: anne@example.com |
1328 | 297 | delivery_mode: regular | 296 | delivery_mode: regular |
1329 | 298 | display_name: Anne Person | 297 | display_name: Anne Person |
1330 | 299 | http_etag: "..." | 298 | http_etag: "..." |
1331 | 300 | language: en | 299 | language: en |
1332 | 301 | password: password | 300 | password: password |
1334 | 302 | request_id: 1 | 301 | request_id: ... |
1335 | 303 | type: subscription | 302 | type: subscription |
1336 | 304 | when: 2005-08-01T07:49:23 | 303 | when: 2005-08-01T07:49:23 |
1337 | 305 | 304 | ||
1338 | 306 | Bart's unsubscription request looks like this. | 305 | Bart's unsubscription request looks like this. |
1339 | 307 | 306 | ||
1341 | 308 | >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests/2') | 307 | >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' |
1342 | 308 | ... 'requests/{}'.format(unsub_req_id)) | ||
1343 | 309 | address: bart@example.com | 309 | address: bart@example.com |
1344 | 310 | http_etag: "..." | 310 | http_etag: "..." |
1346 | 311 | request_id: 2 | 311 | request_id: ... |
1347 | 312 | type: unsubscription | 312 | type: unsubscription |
1348 | 313 | 313 | ||
1349 | 314 | 314 | ||
1350 | @@ -328,9 +328,8 @@ | |||
1351 | 328 | Anne's subscription request is accepted. | 328 | Anne's subscription request is accepted. |
1352 | 329 | 329 | ||
1353 | 330 | >>> dump_json('http://localhost:9001/3.0/lists/' | 330 | >>> dump_json('http://localhost:9001/3.0/lists/' |
1357 | 331 | ... 'ant@example.com/requests/1', { | 331 | ... 'ant@example.com/requests/{}'.format(sub_req_id), |
1358 | 332 | ... 'action': 'accept', | 332 | ... {'action': 'accept'}) |
1356 | 333 | ... }) | ||
1359 | 334 | content-length: 0 | 333 | content-length: 0 |
1360 | 335 | date: ... | 334 | date: ... |
1361 | 336 | server: ... | 335 | server: ... |
1362 | @@ -347,9 +346,8 @@ | |||
1363 | 347 | Bart's unsubscription request is discarded. | 346 | Bart's unsubscription request is discarded. |
1364 | 348 | 347 | ||
1365 | 349 | >>> dump_json('http://localhost:9001/3.0/lists/' | 348 | >>> dump_json('http://localhost:9001/3.0/lists/' |
1369 | 350 | ... 'ant@example.com/requests/2', { | 349 | ... 'ant@example.com/requests/{}'.format(unsub_req_id), |
1370 | 351 | ... 'action': 'discard', | 350 | ... {'action': 'discard'}) |
1368 | 352 | ... }) | ||
1371 | 353 | content-length: 0 | 351 | content-length: 0 |
1372 | 354 | date: ... | 352 | date: ... |
1373 | 355 | server: ... | 353 | server: ... |
1374 | 356 | 354 | ||
1375 | === modified file 'src/mailman/testing/layers.py' | |||
1376 | --- src/mailman/testing/layers.py 2014-10-06 17:17:50 +0000 | |||
1377 | +++ src/mailman/testing/layers.py 2014-10-31 13:23:25 +0000 | |||
1378 | @@ -47,16 +47,13 @@ | |||
1379 | 47 | 47 | ||
1380 | 48 | from lazr.config import as_boolean | 48 | from lazr.config import as_boolean |
1381 | 49 | from pkg_resources import resource_string | 49 | from pkg_resources import resource_string |
1382 | 50 | from sqlalchemy import MetaData | ||
1383 | 51 | from textwrap import dedent | 50 | from textwrap import dedent |
1384 | 52 | from zope import event | ||
1385 | 53 | from zope.component import getUtility | 51 | from zope.component import getUtility |
1386 | 54 | 52 | ||
1387 | 55 | from mailman.config import config | 53 | from mailman.config import config |
1388 | 56 | from mailman.core import initialize | 54 | from mailman.core import initialize |
1389 | 57 | from mailman.core.initialize import INHIBIT_CONFIG_FILE | 55 | from mailman.core.initialize import INHIBIT_CONFIG_FILE |
1390 | 58 | from mailman.core.logging import get_handler | 56 | from mailman.core.logging import get_handler |
1391 | 59 | from mailman.database.model import Model | ||
1392 | 60 | from mailman.database.transaction import transaction | 57 | from mailman.database.transaction import transaction |
1393 | 61 | from mailman.interfaces.domain import IDomainManager | 58 | from mailman.interfaces.domain import IDomainManager |
1394 | 62 | from mailman.testing.helpers import ( | 59 | from mailman.testing.helpers import ( |
1395 | @@ -101,9 +98,7 @@ | |||
1396 | 101 | # Set up the basic configuration stuff. Turn off path creation until | 98 | # Set up the basic configuration stuff. Turn off path creation until |
1397 | 102 | # we've pushed the testing config. | 99 | # we've pushed the testing config. |
1398 | 103 | config.create_paths = False | 100 | config.create_paths = False |
1402 | 104 | if not event.subscribers: | 101 | initialize.initialize_1(INHIBIT_CONFIG_FILE) |
1400 | 105 | # only if not yet initialized by another layer | ||
1401 | 106 | initialize.initialize_1(INHIBIT_CONFIG_FILE) | ||
1403 | 107 | assert cls.var_dir is None, 'Layer already set up' | 102 | assert cls.var_dir is None, 'Layer already set up' |
1404 | 108 | # Calculate a temporary VAR_DIR directory so that run-time artifacts | 103 | # Calculate a temporary VAR_DIR directory so that run-time artifacts |
1405 | 109 | # of the tests won't tread on the installation's data. This also | 104 | # of the tests won't tread on the installation's data. This also |
1406 | @@ -195,10 +190,11 @@ | |||
1407 | 195 | @classmethod | 190 | @classmethod |
1408 | 196 | def tearDown(cls): | 191 | def tearDown(cls): |
1409 | 197 | assert cls.var_dir is not None, 'Layer not set up' | 192 | assert cls.var_dir is not None, 'Layer not set up' |
1411 | 198 | # Reset the test database after the tests are done so that there is no | 193 | reset_the_world() |
1412 | 194 | # Destroy the test database after the tests are done so that there is no | ||
1413 | 199 | # data in case the tests are rerun with a database layer like mysql or | 195 | # data in case the tests are rerun with a database layer like mysql or |
1414 | 200 | # postgresql which are not deleted in teardown. | 196 | # postgresql which are not deleted in teardown. |
1416 | 201 | reset_the_world() | 197 | config.db.destroy() |
1417 | 202 | config.pop('test config') | 198 | config.pop('test config') |
1418 | 203 | shutil.rmtree(cls.var_dir) | 199 | shutil.rmtree(cls.var_dir) |
1419 | 204 | cls.var_dir = None | 200 | cls.var_dir = None |
1420 | 205 | 201 | ||
1421 | === modified file 'src/mailman/testing/testing.cfg' | |||
1422 | --- src/mailman/testing/testing.cfg 2014-10-10 04:59:43 +0000 | |||
1423 | +++ src/mailman/testing/testing.cfg 2014-10-31 13:23:25 +0000 | |||
1424 | @@ -20,7 +20,7 @@ | |||
1425 | 20 | # For testing against PostgreSQL. | 20 | # For testing against PostgreSQL. |
1426 | 21 | # [database] | 21 | # [database] |
1427 | 22 | # class: mailman.database.postgresql.PostgreSQLDatabase | 22 | # class: mailman.database.postgresql.PostgreSQLDatabase |
1429 | 23 | # url: postgresql://maxking:maxking@localhost/mailman_test | 23 | # url: postgresql://$USER:$USER@localhost/mailman_test |
1430 | 24 | 24 | ||
1431 | 25 | [mailman] | 25 | [mailman] |
1432 | 26 | site_owner: noreply@example.com | 26 | site_owner: noreply@example.com |
1433 | 27 | 27 | ||
1434 | === modified file 'src/mailman/utilities/importer.py' | |||
1435 | --- src/mailman/utilities/importer.py 2014-09-28 00:17:05 +0000 | |||
1436 | +++ src/mailman/utilities/importer.py 2014-10-31 13:23:25 +0000 | |||
1437 | @@ -175,6 +175,7 @@ | |||
1438 | 175 | allow_list_posts=bool, | 175 | allow_list_posts=bool, |
1439 | 176 | include_rfc2369_headers=bool, | 176 | include_rfc2369_headers=bool, |
1440 | 177 | nntp_prefix_subject_too=bool, | 177 | nntp_prefix_subject_too=bool, |
1441 | 178 | encode_ascii_prefixes=bool, | ||
1442 | 178 | ) | 179 | ) |
1443 | 179 | 180 | ||
1444 | 180 | 181 | ||
1445 | 181 | 182 | ||
1446 | === modified file 'src/mailman/utilities/tests/test_import.py' | |||
1447 | --- src/mailman/utilities/tests/test_import.py 2014-04-28 15:23:35 +0000 | |||
1448 | +++ src/mailman/utilities/tests/test_import.py 2014-10-31 13:23:25 +0000 | |||
1449 | @@ -34,6 +34,7 @@ | |||
1450 | 34 | from datetime import timedelta, datetime | 34 | from datetime import timedelta, datetime |
1451 | 35 | from enum import Enum | 35 | from enum import Enum |
1452 | 36 | from pkg_resources import resource_filename | 36 | from pkg_resources import resource_filename |
1453 | 37 | from sqlalchemy.exc import IntegrityError | ||
1454 | 37 | from zope.component import getUtility | 38 | from zope.component import getUtility |
1455 | 38 | 39 | ||
1456 | 39 | from mailman.app.lifecycle import create_list | 40 | from mailman.app.lifecycle import create_list |
1457 | @@ -291,6 +292,15 @@ | |||
1458 | 291 | else: | 292 | else: |
1459 | 292 | self.fail('Import21Error was not raised') | 293 | self.fail('Import21Error was not raised') |
1460 | 293 | 294 | ||
1461 | 295 | def test_encode_ascii_prefixes(self): | ||
1462 | 296 | self._pckdict['encode_ascii_prefixes'] = 2 | ||
1463 | 297 | self.assertEqual(self._mlist.encode_ascii_prefixes, False) | ||
1464 | 298 | try: | ||
1465 | 299 | self._import() | ||
1466 | 300 | except IntegrityError as e: | ||
1467 | 301 | self.fail(e) | ||
1468 | 302 | self.assertEqual(self._mlist.encode_ascii_prefixes, True) | ||
1469 | 303 | |||
1470 | 294 | 304 | ||
1471 | 295 | 305 | ||
1472 | 296 | 306 | ||
1473 | 297 | class TestArchiveImport(unittest.TestCase): | 307 | class TestArchiveImport(unittest.TestCase): |