Compare commits
559 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caf5bdbf89 | ||
|
|
6ea944a350 | ||
|
|
9d47521cf0 | ||
|
|
86440b1853 | ||
|
|
e98b15eb67 | ||
|
|
1ffe0e7b82 | ||
|
|
3c069544fc | ||
|
|
4d4344212f | ||
|
|
eddd16d9fd | ||
|
|
37a2898f18 | ||
|
|
af5e18e26c | ||
|
|
320835d43f | ||
|
|
2664230fad | ||
|
|
48d818742c | ||
|
|
2b13836efa | ||
|
|
195d066ff8 | ||
|
|
1e8b59e39f | ||
|
|
b28c439494 | ||
|
|
223a04be27 | ||
|
|
3aefaff44f | ||
|
|
f9c5b00ffa | ||
|
|
e533e63bbf | ||
|
|
be582b5f53 | ||
|
|
4a489af0ff | ||
|
|
c19f51a3d7 | ||
|
|
ca9b320c9c | ||
|
|
ace2f975ea | ||
|
|
e3250f4846 | ||
|
|
29217a47f4 | ||
|
|
5f9876d54e | ||
|
|
8c0dfb525d | ||
|
|
aa4308883c | ||
|
|
381b81b0e1 | ||
|
|
7335c4d06d | ||
|
|
f554fa03ae | ||
|
|
6fa58fd8c9 | ||
|
|
c57a24774d | ||
|
|
d51000b15d | ||
|
|
b8db15a94f | ||
|
|
2f50ed3e99 | ||
|
|
da52304a6e | ||
|
|
f8d3a80af5 | ||
|
|
df744e9182 | ||
|
|
d82d65d95e | ||
|
|
f856db29dc | ||
|
|
4d75c703a0 | ||
|
|
3a8d13599e | ||
|
|
504e42a62e | ||
|
|
149f9e1042 | ||
|
|
3f1efe1b57 | ||
|
|
72de7d94fd | ||
|
|
c3233b9c15 | ||
|
|
2995d65720 | ||
|
|
77a7d576ec | ||
|
|
3b30c5b67a | ||
|
|
610cde6f85 | ||
|
|
add518e6b6 | ||
|
|
bc6ead1a3c | ||
|
|
0c04577f9f | ||
|
|
523704f890 | ||
|
|
6951da7da0 | ||
|
|
d83bf93154 | ||
|
|
4437ecb385 | ||
|
|
d1c8209875 | ||
|
|
6231bb0da3 | ||
|
|
64388832d9 | ||
|
|
d00b62e0f4 | ||
|
|
804dcd3521 | ||
|
|
c1d0fc9aaf | ||
|
|
8ece058256 | ||
|
|
10b8de060a | ||
|
|
10baa7f8af | ||
|
|
740a668f52 | ||
|
|
c56f4f9444 | ||
|
|
bdd1006e06 | ||
|
|
46f72d9350 | ||
|
|
f8404be1b2 | ||
|
|
2e82ca5fde | ||
|
|
a07e494554 | ||
|
|
75d80ca183 | ||
|
|
395bee4bc0 | ||
|
|
b66b5e2715 | ||
|
|
73d9900844 | ||
|
|
6cc89c9fcf | ||
|
|
2e36e2619f | ||
|
|
3042d1442a | ||
|
|
312238c023 | ||
|
|
ff2b893d31 | ||
|
|
c953c7d313 | ||
|
|
fbcf9ec543 | ||
|
|
9173b60677 | ||
|
|
7fc7b4307b | ||
|
|
545aab85f2 | ||
|
|
fa074da5a9 | ||
|
|
29a5b7452e | ||
|
|
94ce5b0a5a | ||
|
|
93712a3ce6 | ||
|
|
6f697294b2 | ||
|
|
7cba3a07af | ||
|
|
4f7ef0b63f | ||
|
|
67ec87e7f9 | ||
|
|
578cba20d4 | ||
|
|
93f91c9607 | ||
|
|
925f75088d | ||
|
|
eac0345689 | ||
|
|
20424a9c76 | ||
|
|
2229d2d947 | ||
|
|
c3bd29b490 | ||
|
|
f552749de6 | ||
|
|
cf255cd643 | ||
|
|
10296faff1 | ||
|
|
9f5e6f9761 | ||
|
|
c790288a5f | ||
|
|
da097c9d67 | ||
|
|
4f5d7948f7 | ||
|
|
1a97c59439 | ||
|
|
e71685736e | ||
|
|
5876736890 | ||
|
|
f97e082543 | ||
|
|
91880ffc19 | ||
|
|
169d8d1e54 | ||
|
|
11bf293972 | ||
|
|
9cc6b602b9 | ||
|
|
3d70917758 | ||
|
|
0cebee2d24 | ||
|
|
379390a8aa | ||
|
|
8ba2a98e11 | ||
|
|
0f5ed3abc7 | ||
|
|
74b8fdf28a | ||
|
|
f266c8f92f | ||
|
|
afdb928b12 | ||
|
|
1706947edd | ||
|
|
54afd26c7e | ||
|
|
05ab5ad6d5 | ||
|
|
a127767da6 | ||
|
|
a7d646c5e7 | ||
|
|
87fcea7eb7 | ||
|
|
ee036223ce | ||
|
|
325cb0aa49 | ||
|
|
c933ada7fb | ||
|
|
28150c7486 | ||
|
|
dd7fd97810 | ||
|
|
1a8b2838fa | ||
|
|
82d4aae571 | ||
|
|
fd473d4002 | ||
|
|
45e10f4c48 | ||
|
|
3efb8028fb | ||
|
|
b8247bc91e | ||
|
|
2630a452b4 | ||
|
|
b3ce06bbf9 | ||
|
|
962d0fe2be | ||
|
|
811408959f | ||
|
|
25ed74a77a | ||
|
|
5ece3858e4 | ||
|
|
1c1623885f | ||
|
|
5dc66bb4ca | ||
|
|
030548bc73 | ||
|
|
78d67c007b | ||
|
|
d93016d85f | ||
|
|
f2be437daa | ||
|
|
22a50a5b88 | ||
|
|
37cdf17e0e | ||
|
|
78d33a2f28 | ||
|
|
d5e463605e | ||
|
|
0d0a7b7fec | ||
|
|
f691292aaa | ||
|
|
ed7a2d2da3 | ||
|
|
8131f5bdc0 | ||
|
|
c8d78177b9 | ||
|
|
f6a04b92d2 | ||
|
|
7084313408 | ||
|
|
7629b8fda7 | ||
|
|
1771cb0af8 | ||
|
|
d1b45ef3d4 | ||
|
|
f16d54f9a8 | ||
|
|
d909aead4e | ||
|
|
0b24a70279 | ||
|
|
b3a05b545e | ||
|
|
221ac3e466 | ||
|
|
2ee6653ff7 | ||
|
|
4337702a6a | ||
|
|
ff90b1b4fb | ||
|
|
85d35f7418 | ||
|
|
cbb4c0dadc | ||
|
|
c10f0c2c36 | ||
|
|
6e84b487ca | ||
|
|
061f4c1515 | ||
|
|
fe34a8a15a | ||
|
|
5715bbd6f5 | ||
|
|
10f6c3a432 | ||
|
|
536252cb2e | ||
|
|
7cd899fd3c | ||
|
|
e45cd7e35e | ||
|
|
75a6bf2395 | ||
|
|
1d28dcf140 | ||
|
|
d994c51ccd | ||
|
|
fe95ad0aa6 | ||
|
|
c75d0faa6f | ||
|
|
3608d722fa | ||
|
|
18b5ddc4dd | ||
|
|
a2fbe31a26 | ||
|
|
222a08ec03 | ||
|
|
c7326f1949 | ||
|
|
3bca6be46d | ||
|
|
13488dd540 | ||
|
|
ac1eed545c | ||
|
|
e09e7148a3 | ||
|
|
d163d92b33 | ||
|
|
105ed6dcaa | ||
|
|
a3d15b2c60 | ||
|
|
3234e6e978 | ||
|
|
02c129df7a | ||
|
|
f95877a09b | ||
|
|
7bd60b5abb | ||
|
|
33d78f41b2 | ||
|
|
9d64d7e27a | ||
|
|
a2af204687 | ||
|
|
4d5ea7be43 | ||
|
|
98608611af | ||
|
|
b18ab6e03b | ||
|
|
17bd875444 | ||
|
|
aa6d2a37e6 | ||
|
|
14feeb9ca1 | ||
|
|
f7c562e492 | ||
|
|
53fc3204fb | ||
|
|
6756416d69 | ||
|
|
d3c0ff3a1f | ||
|
|
30b756a1a2 | ||
|
|
49f7972d48 | ||
|
|
1e76202b65 | ||
|
|
55fcc5a219 | ||
|
|
866255e1f5 | ||
|
|
0149771997 | ||
|
|
89068d9471 | ||
|
|
d483f18374 | ||
|
|
95de17b652 | ||
|
|
f5e9d91f7b | ||
|
|
09671eb6fc | ||
|
|
6f0db1d193 | ||
|
|
76e49b57bf | ||
|
|
4a568f43fe | ||
|
|
ee2f292efa | ||
|
|
1ff37207a2 | ||
|
|
7747655905 | ||
|
|
030986dcc4 | ||
|
|
9da1ba40ed | ||
|
|
5c27076d32 | ||
|
|
40f41496d8 | ||
|
|
d4f5073076 | ||
|
|
59f3adb46b | ||
|
|
d53572a710 | ||
|
|
676081b87a | ||
|
|
a9fe84d9b9 | ||
|
|
1b0d6296dd | ||
|
|
8d9e0712be | ||
|
|
3296939eda | ||
|
|
b6909e61a0 | ||
|
|
ba1451330e | ||
|
|
352e305431 | ||
|
|
3a23baf484 | ||
|
|
99301a0dae | ||
|
|
3029aa6558 | ||
|
|
45c1517580 | ||
|
|
3239bd250b | ||
|
|
ff5c1b6611 | ||
|
|
018254a907 | ||
|
|
9fcd108091 | ||
|
|
27aa14c20f | ||
|
|
d541713dca | ||
|
|
0b2d9a5520 | ||
|
|
c6c7d041b7 | ||
|
|
1b34492108 | ||
|
|
eebeb1b257 | ||
|
|
56a936993c | ||
|
|
1b6391d814 | ||
|
|
f3e62e38aa | ||
|
|
a20e60aeae | ||
|
|
fd76cd8f41 | ||
|
|
e0dd171e45 | ||
|
|
47df1e16b6 | ||
|
|
f8b5951103 | ||
|
|
b62978a88f | ||
|
|
2752168a58 | ||
|
|
eb4249322e | ||
|
|
9fa6ca885a | ||
|
|
587951966f | ||
|
|
fa0c7b18bf | ||
|
|
1103f2ad62 | ||
|
|
a42d9eb9f6 | ||
|
|
6495007aba | ||
|
|
94bc770786 | ||
|
|
77667c11d2 | ||
|
|
f5ea19858c | ||
|
|
d3b20e8d24 | ||
|
|
30bbf3b042 | ||
|
|
89f178bf4d | ||
|
|
4656bc4c97 | ||
|
|
f02b84d528 | ||
|
|
a15afb5e48 | ||
|
|
ae63605ac0 | ||
|
|
631dfe9f13 | ||
|
|
9d01975c78 | ||
|
|
77b640d1b7 | ||
|
|
9be7d78fb1 | ||
|
|
c348cec481 | ||
|
|
79af98fc29 | ||
|
|
7780e74016 | ||
|
|
db21cac694 | ||
|
|
080fd2880e | ||
|
|
71e332c9c4 | ||
|
|
425e4849f3 | ||
|
|
8737254a74 | ||
|
|
0361b73d75 | ||
|
|
27bcd2dbda | ||
|
|
cc82f1601c | ||
|
|
9239d2dd06 | ||
|
|
0bfd3819c8 | ||
|
|
abb05d9384 | ||
|
|
516cee2a94 | ||
|
|
d28838bbb6 | ||
|
|
88e61467f1 | ||
|
|
6d46efa87a | ||
|
|
cd96e52144 | ||
|
|
7864f6a4fd | ||
|
|
51a684f488 | ||
|
|
f254b6f7c1 | ||
|
|
98863d1d01 | ||
|
|
8a5c3c0c69 | ||
|
|
1580b2c8da | ||
|
|
4b1db63b35 | ||
|
|
ee1064b13c | ||
|
|
2ca39443a3 | ||
|
|
7637a6ecda | ||
|
|
15ed79cb08 | ||
|
|
1f45edf121 | ||
|
|
213c9fa119 | ||
|
|
b44e04f28d | ||
|
|
f93c7e0cc4 | ||
|
|
90306e0089 | ||
|
|
ec96ab5286 | ||
|
|
56c1391a16 | ||
|
|
bb755ae009 | ||
|
|
31bba4e087 | ||
|
|
9d5b313aad | ||
|
|
8c6593cc08 | ||
|
|
9652be0ac1 | ||
|
|
79a9a99d1e | ||
|
|
ed03eef81b | ||
|
|
0e03ae1ee8 | ||
|
|
8292ff7d0f | ||
|
|
bcede77e45 | ||
|
|
ecb19b751a | ||
|
|
d1dcf0fa92 | ||
|
|
0060869e79 | ||
|
|
452ce50d7d | ||
|
|
6d9e3ac686 | ||
|
|
3adf6687c9 | ||
|
|
41a572ee1e | ||
|
|
1a86d09da4 | ||
|
|
f4ec2d1ecd | ||
|
|
a4a652af85 | ||
|
|
e6f2258409 | ||
|
|
e0bca8fe51 | ||
|
|
a5a94c4e8f | ||
|
|
b04a04cabd | ||
|
|
4dd6c7679d | ||
|
|
e584892c12 | ||
|
|
6a82cdb37f | ||
|
|
609776bf26 | ||
|
|
30710a9cd6 | ||
|
|
c02719f44c | ||
|
|
7aee121bd7 | ||
|
|
270ac2bc0d | ||
|
|
92d482069c | ||
|
|
cc15685015 | ||
|
|
455b0afdfe | ||
|
|
690ccaedc1 | ||
|
|
58e1693af0 | ||
|
|
69dd2d7a78 | ||
|
|
86fc12dd15 | ||
|
|
1f01fafec7 | ||
|
|
92cab2bae7 | ||
|
|
37295dff0a | ||
|
|
84efdacfc0 | ||
|
|
d2e6cd0523 | ||
|
|
33bd39053f | ||
|
|
cc3404b832 | ||
|
|
2b1505c0f3 | ||
|
|
09ba42a1bb | ||
|
|
a161a865c8 | ||
|
|
fb159b0d40 | ||
|
|
6f80c985fb | ||
|
|
5f3bdf2d0b | ||
|
|
8e5efa7d6d | ||
|
|
82caacd633 | ||
|
|
6c2b1c4363 | ||
|
|
861f34fe90 | ||
|
|
4a76c096da | ||
|
|
9ac4faf3af | ||
|
|
118eebb190 | ||
|
|
9910db2ca6 | ||
|
|
fabd967595 | ||
|
|
c38bb72205 | ||
|
|
80f3e33e41 | ||
|
|
3795b08e95 | ||
|
|
eab7e039eb | ||
|
|
6f5918f03b | ||
|
|
d8b9b5f1c8 | ||
|
|
3ca770b420 | ||
|
|
5e21e39125 | ||
|
|
df8aedba47 | ||
|
|
1bdc0896ca | ||
|
|
90f39426b4 | ||
|
|
f10bfbb7e5 | ||
|
|
a603021757 | ||
|
|
c15a1b698c | ||
|
|
6e11f8cd2a | ||
|
|
5791c06bf2 | ||
|
|
db1ff48996 | ||
|
|
1f29529a24 | ||
|
|
ab9f6a75ad | ||
|
|
496e3b1138 | ||
|
|
aed66120a2 | ||
|
|
5914277f92 | ||
|
|
98b2300266 | ||
|
|
2986c5dc74 | ||
|
|
7466516673 | ||
|
|
2c523cd0d6 | ||
|
|
40e91dcd85 | ||
|
|
3d2975f38e | ||
|
|
c3fd7f0247 | ||
|
|
cb5bd868d9 | ||
|
|
0f96be372d | ||
|
|
4d598370b4 | ||
|
|
8f69d23f18 | ||
|
|
1e71a5c392 | ||
|
|
b3ca12d435 | ||
|
|
d80f37f14a | ||
|
|
8af3ede092 | ||
|
|
6ddea4a1bc | ||
|
|
e3b34c9da3 | ||
|
|
1b97d06a09 | ||
|
|
9fd1367845 | ||
|
|
62a4cf68e8 | ||
|
|
81062c5e2f | ||
|
|
47bad98c07 | ||
|
|
d4d0226058 | ||
|
|
22c1ad9f7b | ||
|
|
6d84da39e4 | ||
|
|
5df58c619d | ||
|
|
8ea2364039 | ||
|
|
b70c7a209d | ||
|
|
f9eadc6440 | ||
|
|
0f29b503ef | ||
|
|
29d437489d | ||
|
|
3c3002ccd5 | ||
|
|
c8119d89b6 | ||
|
|
5cf27098cf | ||
|
|
734a40eb28 | ||
|
|
518194537e | ||
|
|
783c714a2c | ||
|
|
7ab98c1b25 | ||
|
|
bde56a8246 | ||
|
|
1e51844519 | ||
|
|
32e2bf7d08 | ||
|
|
a0d7ab2244 | ||
|
|
6823fa634b | ||
|
|
5851471009 | ||
|
|
45b98d4915 | ||
|
|
99af33b26d | ||
|
|
1c9760d123 | ||
|
|
86da34b874 | ||
|
|
499a982860 | ||
|
|
c7f11745cd | ||
|
|
6c3e961bc5 | ||
|
|
73fc2f01f2 | ||
|
|
e30aeed876 | ||
|
|
5d2504df0a | ||
|
|
f636ed8ced | ||
|
|
c1fcf9c4d8 | ||
|
|
43b8bd7df7 | ||
|
|
54709bcae1 | ||
|
|
c791610998 | ||
|
|
e336683cdb | ||
|
|
749bfa46c4 | ||
|
|
318a935b89 | ||
|
|
04dd0810c3 | ||
|
|
ee17858cce | ||
|
|
ee86950af4 | ||
|
|
a051eb1a05 | ||
|
|
ad0c2edfd2 | ||
|
|
e63abd631f | ||
|
|
1781347999 | ||
|
|
01848ca821 | ||
|
|
39eac1be28 | ||
|
|
c95504e738 | ||
|
|
59892b8532 | ||
|
|
b3e8b2f6ab | ||
|
|
7a4bd92e33 | ||
|
|
711552e84c | ||
|
|
9af223e6cb | ||
|
|
8a6a3183ae | ||
|
|
9018fe40e3 | ||
|
|
4369c65790 | ||
|
|
fb365e47dc | ||
|
|
45a9aa536f | ||
|
|
ee1747fcb4 | ||
|
|
6f4c6d36a4 | ||
|
|
78d0d4656b | ||
|
|
b930565d56 | ||
|
|
0f7a9e2da2 | ||
|
|
96ec12902d | ||
|
|
1dafcc6b37 | ||
|
|
d75f7b2dd7 | ||
|
|
c2d0c52086 | ||
|
|
0014e65c1d | ||
|
|
fc3178a394 | ||
|
|
ac934bb2b6 | ||
|
|
695c8038e0 | ||
|
|
e596d9df13 | ||
|
|
130046d245 | ||
|
|
e0e9953be2 | ||
|
|
3bdbb29c6f | ||
|
|
c23cc3656c | ||
|
|
783e6aeb4d | ||
|
|
5e95feaf90 | ||
|
|
91f90d27ee | ||
|
|
d1932a30ed | ||
|
|
681d344eac | ||
|
|
13932b7f4b | ||
|
|
e391662cff | ||
|
|
47d56e41ba | ||
|
|
c981ead40e | ||
|
|
e7b86d808f | ||
|
|
81f7891539 | ||
|
|
8ee7bef638 | ||
|
|
94aef0b771 | ||
|
|
e810e1774d | ||
|
|
d43d69e3db | ||
|
|
739edc26b4 | ||
|
|
58a9e71e81 | ||
|
|
27c08ac235 | ||
|
|
2b8f489d60 | ||
|
|
1fcd373bd5 | ||
|
|
046ac957ab | ||
|
|
5118821c10 | ||
|
|
f3fcb5e6d3 | ||
|
|
561fdca3a2 | ||
|
|
079a2327ec | ||
|
|
e051c9a4b8 | ||
|
|
9c6d5080e3 | ||
|
|
523380432a | ||
|
|
58d7f236a7 | ||
|
|
58db35e70c | ||
|
|
cb9b8e4632 | ||
|
|
cd5a89bec9 | ||
|
|
03970e0c4a | ||
|
|
8336df9929 | ||
|
|
4cf46c2723 |
@@ -11,6 +11,14 @@ syntax:glob
|
||||
*.pyo
|
||||
*.swp
|
||||
*.html
|
||||
*.class
|
||||
*.orig
|
||||
*~
|
||||
|
||||
build/
|
||||
dist/
|
||||
py.egg-info
|
||||
issue/
|
||||
env/
|
||||
3rdparty/
|
||||
.tox
|
||||
|
||||
9
.hgtags
9
.hgtags
@@ -17,3 +17,12 @@ e2a60653cb490aeed81bbbd83c070b99401c211c 1.0.0b9
|
||||
5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0
|
||||
7acde360d94b6a2690ce3d03ff39301da84c0a2b 1.0.0
|
||||
6bd221981ac99103002c1cb94fede400d23a96a1 1.0.1
|
||||
4816e8b80602a3fd3a0a120333ad85fbe7d8bab4 1.0.2
|
||||
60c44bdbf093285dc69d5462d4dbb4acad325ca6 1.1.0
|
||||
319187fcda66714c5eb1353492babeec3d3c826f 1.1.1
|
||||
4fc5212f7626a56b9eb6437b5c673f56dd7eb942 1.2.0
|
||||
c143a8c8840a1c68570890c8ac6165bbf92fd3c6 1.2.1
|
||||
eafd3c256e8732dfb0a4d49d051b5b4339858926 1.3.0
|
||||
d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1
|
||||
d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1
|
||||
8b8e7c25a13cf863f01b2dd955978285ae9daf6a 1.3.1
|
||||
|
||||
23
AUTHORS
Normal file
23
AUTHORS
Normal file
@@ -0,0 +1,23 @@
|
||||
Holger Krekel, holger at merlinux eu
|
||||
Benjamin Peterson, benjamin at python org
|
||||
Ronny Pfannschmidt, Ronny.Pfannschmidt at gmx de
|
||||
Guido Wesdorp, johnny at johnnydebris net
|
||||
Samuele Pedroni, pedronis at openend se
|
||||
Carl Friedrich Bolz, cfbolz at gmx de
|
||||
Armin Rigo, arigo at tunes org
|
||||
Maciek Fijalkowski, fijal at genesilico pl
|
||||
Brian Dorsey, briandorsey at gmail com
|
||||
merlinux GmbH, Germany, office at merlinux eu
|
||||
|
||||
Contributors include::
|
||||
|
||||
Ross Lawley
|
||||
Ralf Schmitt
|
||||
Chris Lamb
|
||||
Harald Armin Massa
|
||||
Martijn Faassen
|
||||
Ian Bicking
|
||||
Jan Balster
|
||||
Grig Gheorghiu
|
||||
Bob Ippolito
|
||||
Christian Tismer
|
||||
702
CHANGELOG
Normal file
702
CHANGELOG
Normal file
@@ -0,0 +1,702 @@
|
||||
Changes between 1.3.1 and 1.3.2
|
||||
==================================================
|
||||
|
||||
New features
|
||||
++++++++++++++++++
|
||||
|
||||
- fix issue103: introduce py.test.raises as context manager, examples::
|
||||
|
||||
with py.test.raises(ZeroDivisionError):
|
||||
x = 0
|
||||
1 / x
|
||||
|
||||
with py.test.raises(RuntimeError) as excinfo:
|
||||
call_something()
|
||||
|
||||
# you may do extra checks on excinfo.value|type|traceback here
|
||||
|
||||
(thanks Ronny Pfannschmidt)
|
||||
|
||||
- Funcarg factories can now dynamically apply a marker to a
|
||||
test invocation. This is for example useful if a factory
|
||||
provides parameters to a test which are expected-to-fail::
|
||||
|
||||
def pytest_funcarg__arg(request):
|
||||
request.applymarker(py.test.mark.xfail(reason="flaky config"))
|
||||
...
|
||||
|
||||
def test_function(arg):
|
||||
...
|
||||
|
||||
- improved error reporting on collection and import errors. This makes
|
||||
use of a more general mechanism, namely that for custom test item/collect
|
||||
nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
|
||||
override it to return a string error representation of your choice
|
||||
which is going to be reported as a (red) string.
|
||||
|
||||
- introduce '--junitprefix=STR' option to prepend a prefix
|
||||
to all reports in the junitxml file.
|
||||
|
||||
Bug fixes / Maintenance
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
|
||||
to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
|
||||
you can properly check for their existence in a cross-python manner).
|
||||
- refine --pdb: ignore xfailed tests, unify its TB-reporting and
|
||||
don't display failures again at the end.
|
||||
- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
|
||||
- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
|
||||
- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
|
||||
- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
|
||||
- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
|
||||
- fix py.code.compile(source) to generate unique filenames
|
||||
- fix assertion re-interp problems on PyPy, by defering code
|
||||
compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
|
||||
- fix py.path.local.pyimport() to work with directories
|
||||
- streamline py.path.local.mkdtemp implementation and usage
|
||||
- don't print empty lines when showing junitxml-filename
|
||||
- add optional boolean ignore_errors parameter to py.path.local.remove
|
||||
- fix terminal writing on win32/python2.4
|
||||
- py.process.cmdexec() now tries harder to return properly encoded unicode objects
|
||||
on all python versions
|
||||
- install plain py.test/py.which scripts also for Jython, this helps to
|
||||
get canonical script paths in virtualenv situations
|
||||
- make path.bestrelpath(path) return ".", note that when calling
|
||||
X.bestrelpath the assumption is that X is a directory.
|
||||
- make initial conftest discovery ignore "--" prefixed arguments
|
||||
- fix resultlog plugin when used in an multicpu/multihost xdist situation
|
||||
(thanks Jakub Gustak)
|
||||
- perform distributed testing related reporting in the xdist-plugin
|
||||
rather than having dist-related code in the generic py.test
|
||||
distribution
|
||||
- fix homedir detection on Windows
|
||||
- ship distribute_setup.py version 0.6.13
|
||||
|
||||
Changes between 1.3.0 and 1.3.1
|
||||
==================================================
|
||||
|
||||
New features
|
||||
++++++++++++++++++
|
||||
|
||||
- issue91: introduce new py.test.xfail(reason) helper
|
||||
to imperatively mark a test as expected to fail. Can
|
||||
be used from within setup and test functions. This is
|
||||
useful especially for parametrized tests when certain
|
||||
configurations are expected-to-fail. In this case the
|
||||
declarative approach with the @py.test.mark.xfail cannot
|
||||
be used as it would mark all configurations as xfail.
|
||||
|
||||
- issue102: introduce new --maxfail=NUM option to stop
|
||||
test runs after NUM failures. This is a generalization
|
||||
of the '-x' or '--exitfirst' option which is now equivalent
|
||||
to '--maxfail=1'. Both '-x' and '--maxfail' will
|
||||
now also print a line near the end indicating the Interruption.
|
||||
|
||||
- issue89: allow py.test.mark decorators to be used on classes
|
||||
(class decorators were introduced with python2.6) and
|
||||
also allow to have multiple markers applied at class/module level
|
||||
by specifying a list.
|
||||
|
||||
- improve and refine letter reporting in the progress bar:
|
||||
. pass
|
||||
f failed test
|
||||
s skipped tests (reminder: use for dependency/platform mismatch only)
|
||||
x xfailed test (test that was expected to fail)
|
||||
X xpassed test (test that was expected to fail but passed)
|
||||
|
||||
You can use any combination of 'fsxX' with the '-r' extended
|
||||
reporting option. The xfail/xpass results will show up as
|
||||
skipped tests in the junitxml output - which also fixes
|
||||
issue99.
|
||||
|
||||
- make py.test.cmdline.main() return the exitstatus instead of raising
|
||||
SystemExit and also allow it to be called multiple times. This of
|
||||
course requires that your application and tests are properly teared
|
||||
down and don't have global state.
|
||||
|
||||
Fixes / Maintenance
|
||||
++++++++++++++++++++++
|
||||
|
||||
- improved traceback presentation:
|
||||
- improved and unified reporting for "--tb=short" option
|
||||
- Errors during test module imports are much shorter, (using --tb=short style)
|
||||
- raises shows shorter more relevant tracebacks
|
||||
- --fulltrace now more systematically makes traces longer / inhibits cutting
|
||||
|
||||
- improve support for raises and other dynamically compiled code by
|
||||
manipulating python's linecache.cache instead of the previous
|
||||
rather hacky way of creating custom code objects. This makes
|
||||
it seemlessly work on Jython and PyPy where it previously didn't.
|
||||
|
||||
- fix issue96: make capturing more resilient against Control-C
|
||||
interruptions (involved somewhat substantial refactoring
|
||||
to the underlying capturing functionality to avoid race
|
||||
conditions).
|
||||
|
||||
- fix chaining of conditional skipif/xfail decorators - so it works now
|
||||
as expected to use multiple @py.test.mark.skipif(condition) decorators,
|
||||
including specific reporting which of the conditions lead to skipping.
|
||||
|
||||
- fix issue95: late-import zlib so that it's not required
|
||||
for general py.test startup.
|
||||
|
||||
- fix issue94: make reporting more robust against bogus source code
|
||||
(and internally be more careful when presenting unexpected byte sequences)
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.3.0
|
||||
==================================================
|
||||
|
||||
- deprecate --report option in favour of a new shorter and easier to
|
||||
remember -r option: it takes a string argument consisting of any
|
||||
combination of 'xfsX' characters. They relate to the single chars
|
||||
you see during the dotted progress printing and will print an extra line
|
||||
per test at the end of the test run. This extra line indicates the exact
|
||||
position or test ID that you directly paste to the py.test cmdline in order
|
||||
to re-run a particular test.
|
||||
|
||||
- allow external plugins to register new hooks via the new
|
||||
pytest_addhooks(pluginmanager) hook. The new release of
|
||||
the pytest-xdist plugin for distributed and looponfailing
|
||||
testing requires this feature.
|
||||
|
||||
- add a new pytest_ignore_collect(path, config) hook to allow projects and
|
||||
plugins to define exclusion behaviour for their directory structure -
|
||||
for example you may define in a conftest.py this method::
|
||||
|
||||
def pytest_ignore_collect(path):
|
||||
return path.check(link=1)
|
||||
|
||||
to prevent even a collection try of any tests in symlinked dirs.
|
||||
|
||||
- new pytest_pycollect_makemodule(path, parent) hook for
|
||||
allowing customization of the Module collection object for a
|
||||
matching test module.
|
||||
|
||||
- extend and refine xfail mechanism:
|
||||
``@py.test.mark.xfail(run=False)`` do not run the decorated test
|
||||
``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
|
||||
specifiying ``--runxfail`` on command line virtually ignores xfail markers
|
||||
|
||||
- expose (previously internal) commonly useful methods:
|
||||
py.io.get_terminal_with() -> return terminal width
|
||||
py.io.ansi_print(...) -> print colored/bold text on linux/win32
|
||||
py.io.saferepr(obj) -> return limited representation string
|
||||
|
||||
- expose test outcome related exceptions as py.test.skip.Exception,
|
||||
py.test.raises.Exception etc., useful mostly for plugins
|
||||
doing special outcome interpretation/tweaking
|
||||
|
||||
- (issue85) fix junitxml plugin to handle tests with non-ascii output
|
||||
|
||||
- fix/refine python3 compatibility (thanks Benjamin Peterson)
|
||||
|
||||
- fixes for making the jython/win32 combination work, note however:
|
||||
jython2.5.1/win32 does not provide a command line launcher, see
|
||||
http://bugs.jython.org/issue1491 . See pylib install documentation
|
||||
for how to work around.
|
||||
|
||||
- fixes for handling of unicode exception values and unprintable objects
|
||||
|
||||
- (issue87) fix unboundlocal error in assertionold code
|
||||
|
||||
- (issue86) improve documentation for looponfailing
|
||||
|
||||
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
|
||||
|
||||
- ship distribute_setup.py version 0.6.10
|
||||
|
||||
- added links to the new capturelog and coverage plugins
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.2.0
|
||||
=====================================
|
||||
|
||||
- refined usage and options for "py.cleanup"::
|
||||
|
||||
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
|
||||
py.cleanup -e .swp -e .cache # also remove files with these extensions
|
||||
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
|
||||
py.cleanup -d # also remove empty directories
|
||||
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
|
||||
py.cleanup -n # dry run, only show what would be removed
|
||||
|
||||
- add a new option "py.test --funcargs" which shows available funcargs
|
||||
and their help strings (docstrings on their respective factory function)
|
||||
for a given test path
|
||||
|
||||
- display a short and concise traceback if a funcarg lookup fails
|
||||
|
||||
- early-load "conftest.py" files in non-dot first-level sub directories.
|
||||
allows to conveniently keep and access test-related options in a ``test``
|
||||
subdir and still add command line options.
|
||||
|
||||
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
|
||||
|
||||
- fix issue78: always call python-level teardown functions even if the
|
||||
according setup failed. This includes refinements for calling setup_module/class functions
|
||||
which will now only be called once instead of the previous behaviour where they'd be called
|
||||
multiple times if they raise an exception (including a Skipped exception). Any exception
|
||||
will be re-corded and associated with all tests in the according module/class scope.
|
||||
|
||||
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
|
||||
|
||||
- fix pdb debugging to be in the correct frame on raises-related errors
|
||||
|
||||
- update apipkg.py to fix an issue where recursive imports might
|
||||
unnecessarily break importing
|
||||
|
||||
- fix plugin links
|
||||
|
||||
Changes between 1.2 and 1.1.1
|
||||
=====================================
|
||||
|
||||
- moved dist/looponfailing from py.test core into a new
|
||||
separately released pytest-xdist plugin.
|
||||
|
||||
- new junitxml plugin: --junitxml=path will generate a junit style xml file
|
||||
which is processable e.g. by the Hudson CI system.
|
||||
|
||||
- new option: --genscript=path will generate a standalone py.test script
|
||||
which will not need any libraries installed. thanks to Ralf Schmitt.
|
||||
|
||||
- new option: --ignore will prevent specified path from collection.
|
||||
Can be specified multiple times.
|
||||
|
||||
- new option: --confcutdir=dir will make py.test only consider conftest
|
||||
files that are relative to the specified dir.
|
||||
|
||||
- new funcarg: "pytestconfig" is the pytest config object for access
|
||||
to command line args and can now be easily used in a test.
|
||||
|
||||
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
|
||||
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
|
||||
|
||||
- new "pytestconfig" funcarg allows access to test config object
|
||||
|
||||
- new "pytest_report_header" hook can return additional lines
|
||||
to be displayed at the header of a test run.
|
||||
|
||||
- (experimental) allow "py.test path::name1::name2::..." for pointing
|
||||
to a test within a test collection directly. This might eventually
|
||||
evolve as a full substitute to "-k" specifications.
|
||||
|
||||
- streamlined plugin loading: order is now as documented in
|
||||
customize.html: setuptools, ENV, commandline, conftest.
|
||||
also setuptools entry point names are turned to canonical namees ("pytest_*")
|
||||
|
||||
- automatically skip tests that need 'capfd' but have no os.dup
|
||||
|
||||
- allow pytest_generate_tests to be defined in classes as well
|
||||
|
||||
- deprecate usage of 'disabled' attribute in favour of pytestmark
|
||||
- deprecate definition of Directory, Module, Class and Function nodes
|
||||
in conftest.py files. Use pytest collect hooks instead.
|
||||
|
||||
- collection/item node specific runtest/collect hooks are only called exactly
|
||||
on matching conftest.py files, i.e. ones which are exactly below
|
||||
the filesystem path of an item
|
||||
|
||||
- change: the first pytest_collect_directory hook to return something
|
||||
will now prevent further hooks to be called.
|
||||
|
||||
- change: figleaf plugin now requires --figleaf to run. Also
|
||||
change its long command line options to be a bit shorter (see py.test -h).
|
||||
|
||||
- change: pytest doctest plugin is now enabled by default and has a
|
||||
new option --doctest-glob to set a pattern for file matches.
|
||||
|
||||
- change: remove internal py._* helper vars, only keep py._pydir
|
||||
|
||||
- robustify capturing to survive if custom pytest_runtest_setup
|
||||
code failed and prevented the capturing setup code from running.
|
||||
|
||||
- make py.test.* helpers provided by default plugins visible early -
|
||||
works transparently both for pydoc and for interactive sessions
|
||||
which will regularly see e.g. py.test.mark and py.test.importorskip.
|
||||
|
||||
- simplify internal plugin manager machinery
|
||||
- simplify internal collection tree by introducing a RootCollector node
|
||||
|
||||
- fix assert reinterpreation that sees a call containing "keyword=..."
|
||||
|
||||
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
|
||||
hooks on slaves during dist-testing, report module/session teardown
|
||||
hooks correctly.
|
||||
|
||||
- fix issue65: properly handle dist-testing if no
|
||||
execnet/py lib installed remotely.
|
||||
|
||||
- skip some install-tests if no execnet is available
|
||||
|
||||
- fix docs, fix internal bin/ script generation
|
||||
|
||||
|
||||
Changes between 1.1.1 and 1.1.0
|
||||
=====================================
|
||||
|
||||
- introduce automatic plugin registration via 'pytest11'
|
||||
entrypoints via setuptools' pkg_resources.iter_entry_points
|
||||
|
||||
- fix py.test dist-testing to work with execnet >= 1.0.0b4
|
||||
|
||||
- re-introduce py.test.cmdline.main() for better backward compatibility
|
||||
|
||||
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
|
||||
allow '%' in svn paths, make svnwc.update() default to interactive mode
|
||||
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
|
||||
|
||||
- refine distributed tarball to contain test and no pyc files
|
||||
|
||||
- try harder to have deprecation warnings for py.compat.* accesses
|
||||
report a correct location
|
||||
|
||||
Changes between 1.1.0 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* adjust and improve docs
|
||||
|
||||
* remove py.rest tool and internal namespace - it was
|
||||
never really advertised and can still be used with
|
||||
the old release if needed. If there is interest
|
||||
it could be revived into its own tool i guess.
|
||||
|
||||
* fix issue48 and issue59: raise an Error if the module
|
||||
from an imported test file does not seem to come from
|
||||
the filepath - avoids "same-name" confusion that has
|
||||
been reported repeatedly
|
||||
|
||||
* merged Ronny's nose-compatibility hacks: now
|
||||
nose-style setup_module() and setup() functions are
|
||||
supported
|
||||
|
||||
* introduce generalized py.test.mark function marking
|
||||
|
||||
* reshuffle / refine command line grouping
|
||||
|
||||
* deprecate parser.addgroup in favour of getgroup which creates option group
|
||||
|
||||
* add --report command line option that allows to control showing of skipped/xfailed sections
|
||||
|
||||
* generalized skipping: a new way to mark python functions with skipif or xfail
|
||||
at function, class and modules level based on platform or sys-module attributes.
|
||||
|
||||
* extend py.test.mark decorator to allow for positional args
|
||||
|
||||
* introduce and test "py.cleanup -d" to remove empty directories
|
||||
|
||||
* fix issue #59 - robustify unittest test collection
|
||||
|
||||
* make bpython/help interaction work by adding an __all__ attribute
|
||||
to ApiModule, cleanup initpkg
|
||||
|
||||
* use MIT license for pylib, add some contributors
|
||||
|
||||
* remove py.execnet code and substitute all usages with 'execnet' proper
|
||||
|
||||
* fix issue50 - cached_setup now caches more to expectations
|
||||
for test functions with multiple arguments.
|
||||
|
||||
* merge Jarko's fixes, issue #45 and #46
|
||||
|
||||
* add the ability to specify a path for py.lookup to search in
|
||||
|
||||
* fix a funcarg cached_setup bug probably only occuring
|
||||
in distributed testing and "module" scope with teardown.
|
||||
|
||||
* many fixes and changes for making the code base python3 compatible,
|
||||
many thanks to Benjamin Peterson for helping with this.
|
||||
|
||||
* consolidate builtins implementation to be compatible with >=2.3,
|
||||
add helpers to ease keeping 2 and 3k compatible code
|
||||
|
||||
* deprecate py.compat.doctest|subprocess|textwrap|optparse
|
||||
|
||||
* deprecate py.magic.autopath, remove py/magic directory
|
||||
|
||||
* move pytest assertion handling to py/code and a pytest_assertion
|
||||
plugin, add "--no-assert" option, deprecate py.magic namespaces
|
||||
in favour of (less) py.code ones.
|
||||
|
||||
* consolidate and cleanup py/code classes and files
|
||||
|
||||
* cleanup py/misc, move tests to bin-for-dist
|
||||
|
||||
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
|
||||
|
||||
* consolidate py.log implementation, remove old approach.
|
||||
|
||||
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
||||
text/unicode and byte-streams (uses underlying standard lib io.*
|
||||
if available)
|
||||
|
||||
* make py.unittest_convert helper script available which converts "unittest.py"
|
||||
style files into the simpler assert/direct-test-classes py.test/nosetests
|
||||
style. The script was written by Laura Creighton.
|
||||
|
||||
* simplified internal localpath implementation
|
||||
|
||||
Changes between 1.0.1 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* fixing packaging issues, triggered by fedora redhat packaging,
|
||||
also added doc, examples and contrib dirs to the tarball.
|
||||
|
||||
* added a documentation link to the new django plugin.
|
||||
|
||||
Changes between 1.0.0 and 1.0.1
|
||||
=====================================
|
||||
|
||||
* added a 'pytest_nose' plugin which handles nose.SkipTest,
|
||||
nose-style function/method/generator setup/teardown and
|
||||
tries to report functions correctly.
|
||||
|
||||
* capturing of unicode writes or encoded strings to sys.stdout/err
|
||||
work better, also terminalwriting was adapted and somewhat
|
||||
unified between windows and linux.
|
||||
|
||||
* improved documentation layout and content a lot
|
||||
|
||||
* added a "--help-config" option to show conftest.py / ENV-var names for
|
||||
all longopt cmdline options, and some special conftest.py variables.
|
||||
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
|
||||
|
||||
* fix issue #27: better reporting on non-collectable items given on commandline
|
||||
(e.g. pyc files)
|
||||
|
||||
* fix issue #33: added --version flag (thanks Benjamin Peterson)
|
||||
|
||||
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
|
||||
|
||||
* "Test" prefixed classes are *not* collected by default anymore if they
|
||||
have an __init__ method
|
||||
|
||||
* monkeypatch setenv() now accepts a "prepend" parameter
|
||||
|
||||
* improved reporting of collection error tracebacks
|
||||
|
||||
* simplified multicall mechanism and plugin architecture,
|
||||
renamed some internal methods and argnames
|
||||
|
||||
Changes between 1.0.0b9 and 1.0.0
|
||||
=====================================
|
||||
|
||||
* more terse reporting try to show filesystem path relatively to current dir
|
||||
* improve xfail output a bit
|
||||
|
||||
Changes between 1.0.0b8 and 1.0.0b9
|
||||
=====================================
|
||||
|
||||
* cleanly handle and report final teardown of test setup
|
||||
|
||||
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
|
||||
(thanks Wouter Vanden Hove)
|
||||
|
||||
* setup/teardown or collection problems now show as ERRORs
|
||||
or with big "E"'s in the progress lines. they are reported
|
||||
and counted separately.
|
||||
|
||||
* dist-testing: properly handle test items that get locally
|
||||
collected but cannot be collected on the remote side - often
|
||||
due to platform/dependency reasons
|
||||
|
||||
* simplified py.test.mark API - see keyword plugin documentation
|
||||
|
||||
* integrate better with logging: capturing now by default captures
|
||||
test functions and their immediate setup/teardown in a single stream
|
||||
|
||||
* capsys and capfd funcargs now have a readouterr() and a close() method
|
||||
(underlyingly py.io.StdCapture/FD objects are used which grew a
|
||||
readouterr() method as well to return snapshots of captured out/err)
|
||||
|
||||
* make assert-reinterpretation work better with comparisons not
|
||||
returning bools (reported with numpy from thanks maciej fijalkowski)
|
||||
|
||||
* reworked per-test output capturing into the pytest_iocapture.py plugin
|
||||
and thus removed capturing code from config object
|
||||
|
||||
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
|
||||
|
||||
|
||||
Changes between 1.0.0b7 and 1.0.0b8
|
||||
=====================================
|
||||
|
||||
* pytest_unittest-plugin is now enabled by default
|
||||
|
||||
* introduced pytest_keyboardinterrupt hook and
|
||||
refined pytest_sessionfinish hooked, added tests.
|
||||
|
||||
* workaround a buggy logging module interaction ("closing already closed
|
||||
files"). Thanks to Sridhar Ratnakumar for triggering.
|
||||
|
||||
* if plugins use "py.test.importorskip" for importing
|
||||
a dependency only a warning will be issued instead
|
||||
of exiting the testing process.
|
||||
|
||||
* many improvements to docs:
|
||||
- refined funcargs doc , use the term "factory" instead of "provider"
|
||||
- added a new talk/tutorial doc page
|
||||
- better download page
|
||||
- better plugin docstrings
|
||||
- added new plugins page and automatic doc generation script
|
||||
|
||||
* fixed teardown problem related to partially failing funcarg setups
|
||||
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
|
||||
always invoked even if the "pytest_runtest_setup" failed.
|
||||
|
||||
* tweaked doctest output for docstrings in py modules,
|
||||
thanks Radomir.
|
||||
|
||||
Changes between 1.0.0b3 and 1.0.0b7
|
||||
=============================================
|
||||
|
||||
* renamed py.test.xfail back to py.test.mark.xfail to avoid
|
||||
two ways to decorate for xfail
|
||||
|
||||
* re-added py.test.mark decorator for setting keywords on functions
|
||||
(it was actually documented so removing it was not nice)
|
||||
|
||||
* remove scope-argument from request.addfinalizer() because
|
||||
request.cached_setup has the scope arg. TOOWTDI.
|
||||
|
||||
* perform setup finalization before reporting failures
|
||||
|
||||
* apply modified patches from Andreas Kloeckner to allow
|
||||
test functions to have no func_code (#22) and to make
|
||||
"-k" and function keywords work (#20)
|
||||
|
||||
* apply patch from Daniel Peolzleithner (issue #23)
|
||||
|
||||
* resolve issue #18, multiprocessing.Manager() and
|
||||
redirection clash
|
||||
|
||||
* make __name__ == "__channelexec__" for remote_exec code
|
||||
|
||||
Changes between 1.0.0b1 and 1.0.0b3
|
||||
=============================================
|
||||
|
||||
* plugin classes are removed: one now defines
|
||||
hooks directly in conftest.py or global pytest_*.py
|
||||
files.
|
||||
|
||||
* added new pytest_namespace(config) hook that allows
|
||||
to inject helpers directly to the py.test.* namespace.
|
||||
|
||||
* documented and refined many hooks
|
||||
|
||||
* added new style of generative tests via
|
||||
pytest_generate_tests hook that integrates
|
||||
well with function arguments.
|
||||
|
||||
|
||||
Changes between 0.9.2 and 1.0.0b1
|
||||
=============================================
|
||||
|
||||
* introduced new "funcarg" setup method,
|
||||
see doc/test/funcarg.txt
|
||||
|
||||
* introduced plugin architecuture and many
|
||||
new py.test plugins, see
|
||||
doc/test/plugins.txt
|
||||
|
||||
* teardown_method is now guaranteed to get
|
||||
called after a test method has run.
|
||||
|
||||
* new method: py.test.importorskip(mod,minversion)
|
||||
will either import or call py.test.skip()
|
||||
|
||||
* completely revised internal py.test architecture
|
||||
|
||||
* new py.process.ForkedFunc object allowing to
|
||||
fork execution of a function to a sub process
|
||||
and getting a result back.
|
||||
|
||||
XXX lots of things missing here XXX
|
||||
|
||||
Changes between 0.9.1 and 0.9.2
|
||||
===============================
|
||||
|
||||
* refined installation and metadata, created new setup.py,
|
||||
now based on setuptools/ez_setup (thanks to Ralf Schmitt
|
||||
for his support).
|
||||
|
||||
* improved the way of making py.* scripts available in
|
||||
windows environments, they are now added to the
|
||||
Scripts directory as ".cmd" files.
|
||||
|
||||
* py.path.svnwc.status() now is more complete and
|
||||
uses xml output from the 'svn' command if available
|
||||
(Guido Wesdorp)
|
||||
|
||||
* fix for py.path.svn* to work with svn 1.5
|
||||
(Chris Lamb)
|
||||
|
||||
* fix path.relto(otherpath) method on windows to
|
||||
use normcase for checking if a path is relative.
|
||||
|
||||
* py.test's traceback is better parseable from editors
|
||||
(follows the filenames:LINENO: MSG convention)
|
||||
(thanks to Osmo Salomaa)
|
||||
|
||||
* fix to javascript-generation, "py.test --runbrowser"
|
||||
should work more reliably now
|
||||
|
||||
* removed previously accidentally added
|
||||
py.test.broken and py.test.notimplemented helpers.
|
||||
|
||||
* there now is a py.__version__ attribute
|
||||
|
||||
Changes between 0.9.0 and 0.9.1
|
||||
===============================
|
||||
|
||||
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
|
||||
serve as a reference for developers.
|
||||
|
||||
* allowing + signs in py.path.svn urls [39106]
|
||||
* fixed support for Failed exceptions without excinfo in py.test [39340]
|
||||
* added support for killing processes for Windows (as well as platforms that
|
||||
support os.kill) in py.misc.killproc [39655]
|
||||
* added setup/teardown for generative tests to py.test [40702]
|
||||
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
|
||||
* fixed problem with calling .remove() on wcpaths of non-versioned files in
|
||||
py.path [44248]
|
||||
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
|
||||
* fail to run greenlet tests when pypy is available, but without stackless
|
||||
[45294]
|
||||
* small fixes in rsession tests [45295]
|
||||
* fixed issue with 2.5 type representations in py.test [45483, 45484]
|
||||
* made that internal reporting issues displaying is done atomically in py.test
|
||||
[45518]
|
||||
* made that non-existing files are igored by the py.lookup script [45519]
|
||||
* improved exception name creation in py.test [45535]
|
||||
* made that less threads are used in execnet [merge in 45539]
|
||||
* removed lock required for atomical reporting issue displaying in py.test
|
||||
[45545]
|
||||
* removed globals from execnet [45541, 45547]
|
||||
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
|
||||
get called in 2.5 (py.execnet) [45548]
|
||||
* fixed bug in joining threads in py.execnet's servemain [45549]
|
||||
* refactored py.test.rsession tests to not rely on exact output format anymore
|
||||
[45646]
|
||||
* using repr() on test outcome [45647]
|
||||
* added 'Reason' classes for py.test.skip() [45648, 45649]
|
||||
* killed some unnecessary sanity check in py.test.collect [45655]
|
||||
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
|
||||
usable by Administrators [45901]
|
||||
* added support for locking and non-recursive commits to py.path.svnwc [45994]
|
||||
* locking files in py.execnet to prevent CPython from segfaulting [46010]
|
||||
* added export() method to py.path.svnurl
|
||||
* fixed -d -x in py.test [47277]
|
||||
* fixed argument concatenation problem in py.path.svnwc [49423]
|
||||
* restore py.test behaviour that it exits with code 1 when there are failures
|
||||
[49974]
|
||||
* don't fail on html files that don't have an accompanying .txt file [50606]
|
||||
* fixed 'utestconvert.py < input' [50645]
|
||||
* small fix for code indentation in py.code.source [50755]
|
||||
* fix _docgen.py documentation building [51285]
|
||||
* improved checks for source representation of code blocks in py.test [51292]
|
||||
* added support for passing authentication to py.path.svn* objects [52000,
|
||||
52001]
|
||||
* removed sorted() call for py.apigen tests in favour of [].sort() to support
|
||||
Python 2.3 [52481]
|
||||
195
ISSUES.txt
Normal file
195
ISSUES.txt
Normal file
@@ -0,0 +1,195 @@
|
||||
refine session initialization / fix custom collect crash
|
||||
---------------------------------------------------------------
|
||||
tags: bug 1.4 core xdist
|
||||
|
||||
When calling "py.test path/X" py.test can crash if the collection
|
||||
of that directory is skipped. Calling "py.test path" will give
|
||||
proper output. The reason is that for the very first colitems
|
||||
getinitialnodes() and a collection is done before the fully
|
||||
controlled session and pytest_make_collect_report protocol takes over.
|
||||
Try to remove the redundant getinitialnodes related logic and amend
|
||||
the session collect logic to care for this "initial" case as well.
|
||||
Apart from simplification a side effect the dsession's session
|
||||
and the core session probably converge some more.
|
||||
|
||||
introduce py.test.mark.nocollect
|
||||
-------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
|
||||
for not considering a function for test collection at all.
|
||||
maybe also introduce a py.test.mark.test to explicitely
|
||||
mark a function to become a tested one. Lookup JUnit
|
||||
ways of tagging tests.
|
||||
|
||||
introduce py.test.mark.platform
|
||||
-------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
|
||||
Introduce nice-to-spell platform-skipping, examples:
|
||||
|
||||
@py.test.mark.platform("python3")
|
||||
@py.test.mark.platform("not python3")
|
||||
@py.test.mark.platform("win32 and not python3")
|
||||
@py.test.mark.platform("darwin")
|
||||
@py.test.mark.platform("not (jython and win32)")
|
||||
@py.test.mark.platform("not (jython and win32)", xfail=True)
|
||||
|
||||
etc. Idea is to allow Python expressions which can operate
|
||||
on common spellings for operating systems and python
|
||||
interpreter versions.
|
||||
|
||||
introduce py.test.mark registration
|
||||
-----------------------------------------
|
||||
tags: feature 1.3
|
||||
|
||||
introduce a hook that allows to register a named mark decorator
|
||||
with documentation and add "py.test --marks" to get
|
||||
a list of available marks. Deprecate "dynamic" mark
|
||||
definitions.
|
||||
|
||||
do early-teardown of test modules
|
||||
-----------------------------------------
|
||||
tags: feature 1.3
|
||||
|
||||
currently teardowns are called when the next tests is setup
|
||||
except for the function/method level where interally
|
||||
"teardown_exact" tears down immediately. Generalize
|
||||
this to perform the "neccessary" teardown compared to
|
||||
the "next" test item during teardown - this should
|
||||
get rid of some irritations because otherwise e.g.
|
||||
prints of teardown-code appear in the setup of the next test.
|
||||
|
||||
do recursive walk of conftest.py files?
|
||||
-----------------------------------------
|
||||
tags: feature 1.3
|
||||
|
||||
it maybe makes sense to generally do a recursive search of conftest.py
|
||||
files before command line parsing - this would help to offer the
|
||||
full list of options as applicable to a given test project.
|
||||
|
||||
consider introducing py.test.mark.skip_[not]win32/jython/pyXY
|
||||
-------------------------------------------------------------
|
||||
tags: feature 1.3
|
||||
|
||||
conveniently introduce markers for platforms to
|
||||
have a shorter form for skipping.
|
||||
|
||||
generalize parametrized testing to generate combinations
|
||||
-------------------------------------------------------------
|
||||
tags: feature 1.3
|
||||
|
||||
think about extending metafunc.addcall or add a new method to allow to
|
||||
generate tests with combinations of all generated versions - what to do
|
||||
about "id" and "param" in such combinations though?
|
||||
|
||||
introduce py.test.mark.multi
|
||||
-----------------------------------------
|
||||
tags: feature 1.3
|
||||
|
||||
introduce py.test.mark.multi to specify a number
|
||||
of values for a given function argument.
|
||||
|
||||
have imported module mismatch honour relative paths
|
||||
--------------------------------------------------------
|
||||
tags: bug 1.4
|
||||
|
||||
With 1.1.1 py.test fails at least on windows if an import
|
||||
is relative and compared against an absolute conftest.py
|
||||
path. Normalize.
|
||||
|
||||
make node._checkcollectable more robust
|
||||
-------------------------------------------------
|
||||
tags: bug 1.4
|
||||
|
||||
currently node._checkcollectable() can raise
|
||||
exceptions for all kinds of reasons ('conftest.py' loading
|
||||
problems, missing rsync-dirs, platform-skip-at-import-level
|
||||
issues, ...). It should just return True/False and cause
|
||||
a good error message.
|
||||
|
||||
call termination with small timeout
|
||||
-------------------------------------------------
|
||||
tags: feature 1.4
|
||||
test: testing/pytest/dist/test_dsession.py - test_terminate_on_hanging_node
|
||||
|
||||
Call gateway group termination with a small timeout if available.
|
||||
Should make dist-testing less likely to leave lost processes.
|
||||
|
||||
consider globals: py.test.ensuretemp and config
|
||||
--------------------------------------------------------------
|
||||
tags: experimental-wish 1.4
|
||||
|
||||
consider deprecating py.test.ensuretemp and py.test.config
|
||||
to further reduce py.test globality. Also consider
|
||||
having py.test.config and ensuretemp coming from
|
||||
a plugin rather than being there from the start.
|
||||
|
||||
consider allowing funcargs to setup methods
|
||||
--------------------------------------------------------------
|
||||
tags: experimental-wish 1.4
|
||||
|
||||
Users have expressed the wish to have funcargs available to setup
|
||||
functions. Experiment with allowing funcargs there - it might
|
||||
also help to make the py.test.ensuretemp and config deprecation.
|
||||
For filling funcargs for setup methods, we could call funcarg
|
||||
factories with a request object that not have a cls/function
|
||||
attributes. However, how to handle parametrized test functions
|
||||
and funcargs?
|
||||
|
||||
consider pytest_addsyspath hook
|
||||
-----------------------------------------
|
||||
tags: 1.4
|
||||
|
||||
py.test could call a new pytest_addsyspath() in order to systematically
|
||||
allow manipulation of sys.path and to inhibit it via --no-addsyspath
|
||||
in order to more easily run against installed packages.
|
||||
|
||||
Alternatively it could also be done via the config object
|
||||
and pytest_configure.
|
||||
|
||||
relax requirement to have tests/testing contain an __init__
|
||||
----------------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
bb: http://bitbucket.org/hpk42/py-trunk/issue/64
|
||||
|
||||
A local test run of a "tests" directory may work
|
||||
but a remote one fail because the tests directory
|
||||
does not contain an "__init__.py". Either give
|
||||
an error or make it work without the __init__.py
|
||||
|
||||
|
||||
show plugin information in test header
|
||||
----------------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
|
||||
Now that external plugins are becoming more numerous
|
||||
it would be useful to have external plugins along with
|
||||
their versions displayed as a header line.
|
||||
|
||||
generate/refine plugin doc generation
|
||||
----------------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
|
||||
review and prepare docs for 1.4.0 release. Probably
|
||||
have docs living with the plugin and require them to
|
||||
be available on doc generation time, at least when
|
||||
the target is the website? Or rather go for interactive help?
|
||||
|
||||
deprecate global py.test.config usage
|
||||
----------------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
|
||||
py.test.ensuretemp and py.test.config are probably the last
|
||||
objects containing global state. Often using them is not
|
||||
neccessary. This is about trying to get rid of them, i.e.
|
||||
deprecating them and checking with PyPy's usages as well
|
||||
as others.
|
||||
|
||||
remove deprecated bits in collect.py
|
||||
-------------------------------------------------------------------
|
||||
tags: feature 1.4
|
||||
|
||||
In an effort to further simplify code, review and remove deprecated bits
|
||||
in collect.py. Probably good:
|
||||
- inline consider_file/dir methods, no need to have them
|
||||
subclass-overridable because of hooks
|
||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
24
MANIFEST.in
24
MANIFEST.in
@@ -1,17 +1,19 @@
|
||||
include CHANGELOG
|
||||
include README.txt
|
||||
include setup.py
|
||||
include distribute_setup.py
|
||||
include LICENSE
|
||||
include py/LICENSE
|
||||
include py/path/svn/testing/repotest.dump
|
||||
include py/rest/rest.sty.template
|
||||
exclude *.orig
|
||||
exclude *.rej
|
||||
prune .svn
|
||||
prune .hg
|
||||
include conftest.py
|
||||
graft doc
|
||||
graft contrib
|
||||
graft example
|
||||
graft py/bin
|
||||
graft py/rest/testing/data
|
||||
graft py/misc/testing/data
|
||||
graft bin
|
||||
graft testing
|
||||
#exclude *.orig
|
||||
#exclude *.orig
|
||||
exclude *.rej
|
||||
exclude .hginore
|
||||
exclude *.pyc
|
||||
#recursive-exclude testing *.pyc *.orig *.rej *$py.class
|
||||
#prune .pyc
|
||||
#prune .svn
|
||||
#prune .hg
|
||||
|
||||
@@ -2,7 +2,6 @@ The py lib is a Python development support library featuring
|
||||
the following tools and modules:
|
||||
|
||||
* py.test: tool for distributed automated testing
|
||||
* py.execnet: ad-hoc distributed execution
|
||||
* py.code: dynamic code generation and introspection
|
||||
* py.path: uniform local and svn path objects
|
||||
|
||||
|
||||
4
bin-for-dist/all-plat.sh
Normal file → Executable file
4
bin-for-dist/all-plat.sh
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
py.test --dist=each $* \
|
||||
--tx 'popen//python=python2.6' \
|
||||
--tx 'ssh=noco//python=/usr/local/bin/python2.4//chdir=/tmp/pytest-python2.4' \
|
||||
--tx 'socket=192.168.1.106:8888'
|
||||
#--tx 'popen//python=python2.6' \
|
||||
#--tx 'ssh=noco//python=/usr/local/bin/python2.4//chdir=/tmp/pytest-python2.4' \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from _findpy import py
|
||||
import py
|
||||
|
||||
bindir = py.magic.autopath().dirpath().dirpath("py").join("bin")
|
||||
bindir = py.path.local(__file__).dirpath().dirpath("bin")
|
||||
assert bindir.check(), bindir
|
||||
|
||||
def getbasename(name):
|
||||
@@ -31,5 +31,3 @@ if __name__ == "__main__":
|
||||
if name[0] != "_":
|
||||
genscript_unix(name)
|
||||
genscript_windows(name)
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
sys.path.insert(0, sys.argv[1])
|
||||
import py
|
||||
|
||||
toolpath = py.magic.autopath()
|
||||
toolpath = py.path.local(__file__)
|
||||
binpath = py.path.local(py.__file__).dirpath('bin')
|
||||
|
||||
def error(msg):
|
||||
@@ -27,14 +27,14 @@ class SetupWriter(object):
|
||||
|
||||
def getallpath(self, basedir):
|
||||
contrib = self.basedir.join("contrib")
|
||||
allpath = []
|
||||
lines = py.process.cmdexec("hg st -mcan").split("\n")
|
||||
for path in lines:
|
||||
p = basedir.join(path)
|
||||
assert p.check(), p
|
||||
allpath = []
|
||||
lines = py.process.cmdexec("hg st -mcan").split("\n")
|
||||
for path in lines:
|
||||
p = basedir.join(path)
|
||||
assert p.check(), p
|
||||
if not p.relto(contrib) and p != contrib and not self.isexcluded(p):
|
||||
allpath.append(p)
|
||||
return allpath
|
||||
allpath.append(p)
|
||||
return allpath
|
||||
|
||||
def append(self, string):
|
||||
lines = string.split("\n")
|
||||
@@ -70,37 +70,19 @@ class SetupWriter(object):
|
||||
)
|
||||
for line in output.split("\n"):
|
||||
info.append("%s %s" %(indent, line.strip()))
|
||||
return "\n".join(info)
|
||||
return "\n".join(info)
|
||||
finally:
|
||||
old.chdir()
|
||||
|
||||
def setup_header(self):
|
||||
#tooltime = "%s %s" %(py.std.time.asctime(), py.std.time.tzname[0])
|
||||
toolname = toolpath.basename
|
||||
#toolrevision = py.path.svnwc(toolpath).info().rev
|
||||
|
||||
toolname = toolpath.basename
|
||||
#toolrevision = py.path.svnwc(toolpath).info().rev
|
||||
|
||||
pkgname = self.pkg.__name__
|
||||
info = self.tip_info()
|
||||
self.append('''
|
||||
"""
|
||||
py lib / py.test setup.py file, autogenerated by %(toolname)s
|
||||
''' % locals())
|
||||
#self.append(info)
|
||||
self.append('''
|
||||
"""
|
||||
import os, sys
|
||||
''')
|
||||
|
||||
if self.setuptools:
|
||||
#import ez_setup
|
||||
#ez_setup.use_setuptools()
|
||||
self.append("""
|
||||
from setuptools import setup
|
||||
""")
|
||||
else:
|
||||
self.append("""
|
||||
from distutils.core import setup
|
||||
""")
|
||||
self.append('"""py lib / py.test setup.py file"""')
|
||||
self.append('import os, sys')
|
||||
self.append("from setuptools import setup")
|
||||
|
||||
def setup_trailer(self):
|
||||
self.append('''
|
||||
@@ -111,10 +93,6 @@ class SetupWriter(object):
|
||||
def setup_function(self):
|
||||
params = self.__dict__.copy()
|
||||
params.update(self.meta.__dict__)
|
||||
#params['url'] = self.wcinfo.url
|
||||
#params['rev'] = self.wcinfo.rev
|
||||
#params['long_description'] = reformat(params['long_description'])
|
||||
#print py.std.pprint.pprint(params)
|
||||
self.append('long_description = """')
|
||||
for line in params['long_description'].split('\n'):
|
||||
self.append(line)
|
||||
@@ -128,31 +106,26 @@ class SetupWriter(object):
|
||||
setup(
|
||||
name=%(name)r,
|
||||
description=%(description)r,
|
||||
long_description = long_description,
|
||||
version= trunk or %(version)r,
|
||||
url=%(url)r,
|
||||
long_description = long_description,
|
||||
version= trunk or %(version)r,
|
||||
url=%(url)r,
|
||||
license=%(license)r,
|
||||
platforms=%(platforms)r,
|
||||
platforms=%(platforms)r,
|
||||
author=%(author)r,
|
||||
author_email=%(author_email)r,
|
||||
''' % params)
|
||||
indent = " " * 8
|
||||
#self.append_pprint(indent, py_modules=['_findpy',]),
|
||||
#self.append_pprint(indent, scripts=self.getscripts())
|
||||
self.append_pprint(indent, entry_points={'console_scripts':self.getconsolescripts()})
|
||||
self.append_pprint(indent, classifiers=self.meta.classifiers)
|
||||
self.append_pprint(indent, packages=self.getpackages())
|
||||
#self.append_pprint(indent, data_files=self.getdatafiles())
|
||||
self.append_pprint(indent, package_data=self.getpackagedata())
|
||||
#self.append_pprint(indent, package_dir={'py': 'py'})
|
||||
#self.append_pprint(indent, packages=self.getpackages())
|
||||
if self.setuptools:
|
||||
self.append_pprint(indent, zip_safe=False)
|
||||
self.append_pprint(indent, zip_safe=True)
|
||||
self.append_pprint(indent, install_requires=['apipkg'])
|
||||
self.lines.append(indent[4:] + ")\n")
|
||||
|
||||
def setup_scripts(self):
|
||||
# XXX this was used for a different approach
|
||||
not used
|
||||
not used
|
||||
self.append("""
|
||||
def getscripts():
|
||||
if sys.platform == "win32":
|
||||
@@ -164,7 +137,7 @@ class SetupWriter(object):
|
||||
l = []
|
||||
for name in %r:
|
||||
l.append(base + name + ext)
|
||||
return l
|
||||
return l
|
||||
""" % ([script.basename for script in binpath.listdir("py.*")]))
|
||||
|
||||
def append_pprint(self, indent, append=",", **kw):
|
||||
@@ -187,10 +160,10 @@ class SetupWriter(object):
|
||||
if p.dirpath() == bindir:
|
||||
if p.basename.startswith('py.'):
|
||||
shortname = "py" + p.basename[3:]
|
||||
scripts.append("%s = py.cmdline:%s" %
|
||||
scripts.append("%s = py.cmdline:%s" %
|
||||
(p.basename, shortname))
|
||||
return scripts
|
||||
|
||||
|
||||
def getscripts(self):
|
||||
bindir = self.basedir.join('py', 'bin')
|
||||
scripts = []
|
||||
@@ -202,7 +175,7 @@ class SetupWriter(object):
|
||||
|
||||
def getpackages(self):
|
||||
packages = []
|
||||
for p in self.allpaths: # contains no directories!
|
||||
for p in self.allpaths: # contains no directories!
|
||||
#if p.basename == "py":
|
||||
# continue
|
||||
if p.dirpath('__init__.py').check():
|
||||
@@ -224,7 +197,7 @@ class SetupWriter(object):
|
||||
datafiles = []
|
||||
pkgbase = self.basedir.join(self.pkg.__name__)
|
||||
for p in self.allpaths:
|
||||
if p.check(file=1) and (not p.dirpath("__init__.py").check()
|
||||
if p.check(file=1) and (not p.dirpath("__init__.py").check()
|
||||
or p.ext != ".py"):
|
||||
if p.dirpath() != self.basedir:
|
||||
x = p.relto(pkgbase)
|
||||
@@ -244,7 +217,7 @@ class SetupWriter(object):
|
||||
import winpath
|
||||
self.append(py.std.inspect.getsource(winpath))
|
||||
self.append("""
|
||||
from distutils.command.install import install
|
||||
from distutils.command.install import install
|
||||
class my_install(install):
|
||||
def finalize_other(self):
|
||||
install.finalize_other(self)
|
||||
@@ -281,7 +254,7 @@ class SetupWriter(object):
|
||||
else:
|
||||
cmdclass = {}
|
||||
''')
|
||||
|
||||
|
||||
def write_setup(self):
|
||||
self.setup_header()
|
||||
self.setup_function()
|
||||
|
||||
26
bin-for-dist/hudson.py
Normal file
26
bin-for-dist/hudson.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import os, sys, subprocess, urllib
|
||||
|
||||
BUILDNAME=os.environ.get('BUILD_NUMBER', "1")
|
||||
|
||||
def call(*args):
|
||||
ret = subprocess.call(list(args))
|
||||
assert ret == 0
|
||||
|
||||
def bincall(*args):
|
||||
args = list(args)
|
||||
args[0] = os.path.join(BIN, args[0])
|
||||
call(*args)
|
||||
|
||||
call("virtualenv", os.path.abspath(BUILDNAME), '--no-site-packages')
|
||||
BIN=os.path.abspath(os.path.join(BUILDNAME, 'bin'))
|
||||
if not os.path.exists(BIN):
|
||||
BIN=os.path.abspath(os.path.join(BUILDNAME, 'Scripts'))
|
||||
assert os.path.exists(BIN)
|
||||
|
||||
PYTHON=os.path.join(BIN, 'python')
|
||||
bincall("python", "setup.py", "develop", "-q")
|
||||
bincall("pip", "install", "-r", "testing/pip-reqs1.txt",
|
||||
"-q", "--download-cache=download")
|
||||
bincall("py.test", "--ignore", BUILDNAME,
|
||||
"--xml=junit.xml",
|
||||
"--report=skipped", "--runslowtest", *sys.argv[1:])
|
||||
@@ -1,31 +1,34 @@
|
||||
|
||||
import py
|
||||
import sys
|
||||
import os, sys
|
||||
WIDTH = 75
|
||||
|
||||
plugins = [
|
||||
('plugins for Python test functions',
|
||||
'xfail figleaf monkeypatch capture recwarn',),
|
||||
('plugins for other testing styles and languages',
|
||||
'oejskit unittest nose django doctest restdoc'),
|
||||
('plugins for generic reporting and failure logging',
|
||||
'pastebin resultlog terminal',),
|
||||
('plugins for generic reporting and failure logging',
|
||||
'pastebin resultlog terminal',),
|
||||
('misc plugins / core functionality',
|
||||
'helpconfig pdb keyword hooklog')
|
||||
('advanced python testing',
|
||||
'skipping mark pdb figleaf '
|
||||
'monkeypatch coverage cov capture capturelog recwarn tmpdir',),
|
||||
('distributed testing, CI and deployment',
|
||||
'xdist pastebin junitxml resultlog genscript',),
|
||||
('testing domains and conventions codecheckers',
|
||||
'oejskit django unittest nose doctest restdoc'),
|
||||
('internal, debugging, help functionality',
|
||||
'helpconfig terminal hooklog')
|
||||
#('internal plugins / core functionality',
|
||||
# #'pdb keyword hooklog runner execnetcleanup # pytester',
|
||||
# 'pdb keyword hooklog runner execnetcleanup' # pytester',
|
||||
# #'runner execnetcleanup # pytester',
|
||||
# 'runner execnetcleanup' # pytester',
|
||||
#)
|
||||
]
|
||||
|
||||
externals = {
|
||||
'oejskit': 'run javascript tests in real life browsers',
|
||||
'django': 'support for testing django applications',
|
||||
|
||||
'oejskit': "run javascript tests in real life browsers",
|
||||
'xdist': None,
|
||||
'figleaf': None,
|
||||
'capturelog': None,
|
||||
'coverage': None,
|
||||
'cov': None,
|
||||
'codecheckers': None,
|
||||
'django': "for testing django applications",
|
||||
}
|
||||
|
||||
|
||||
def warn(*args):
|
||||
msg = " ".join(map(str, args))
|
||||
print >>sys.stderr, "WARN:", msg
|
||||
@@ -123,10 +126,10 @@ class RestWriter:
|
||||
self.out.close()
|
||||
print "wrote", self.target
|
||||
del self.out
|
||||
|
||||
|
||||
class PluginOverview(RestWriter):
|
||||
def makerest(self, config):
|
||||
plugindir = py.path.local(py.__file__).dirpath("test", "plugin")
|
||||
plugindir = py._pydir.join('plugin')
|
||||
for cat, specs in plugins:
|
||||
pluginlist = specs.split()
|
||||
self.h1(cat)
|
||||
@@ -135,29 +138,31 @@ class PluginOverview(RestWriter):
|
||||
docpath = self.target.dirpath(name).new(ext=".txt")
|
||||
if oneliner is not None:
|
||||
htmlpath = docpath.new(ext='.html')
|
||||
self.para("%s_ %s" %(name, oneliner))
|
||||
self.para("%s_ (external) %s" %(name, oneliner))
|
||||
self.add_internal_link(name, htmlpath)
|
||||
else:
|
||||
doc = PluginDoc(docpath)
|
||||
doc.make(config=config, name=name)
|
||||
self.add_internal_link(name, doc.target)
|
||||
self.para("%s_ %s" %(name, doc.oneliner))
|
||||
if name in externals:
|
||||
self.para("%s_ (external) %s" %(name, doc.oneliner))
|
||||
else:
|
||||
self.para("%s_ %s" %(name, doc.oneliner))
|
||||
self.Print()
|
||||
|
||||
class HookSpec(RestWriter):
|
||||
|
||||
def makerest(self, config):
|
||||
module = config.pluginmanager.hook._hookspecs
|
||||
source = py.code.Source(module)
|
||||
self.h1("hook specification sourcecode")
|
||||
self.sourcecode(source)
|
||||
for module in config.pluginmanager.hook._hookspecs:
|
||||
source = py.code.Source(module)
|
||||
self.h1("hook specification sourcecode")
|
||||
self.sourcecode(source)
|
||||
|
||||
class PluginDoc(RestWriter):
|
||||
def makerest(self, config, name):
|
||||
config.pluginmanager.import_plugin(name)
|
||||
plugin = config.pluginmanager.getplugin(name)
|
||||
assert plugin is not None, plugin
|
||||
|
||||
print plugin
|
||||
doc = plugin.__doc__.strip()
|
||||
i = doc.find("\n")
|
||||
if i == -1:
|
||||
@@ -167,12 +172,13 @@ class PluginDoc(RestWriter):
|
||||
oneliner = doc[:i].strip()
|
||||
moduledoc = doc[i+1:].strip()
|
||||
|
||||
self.name = plugin.__name__.split(".")[-1]
|
||||
self.name = oneliner # plugin.__name__.split(".")[-1]
|
||||
self.oneliner = oneliner
|
||||
self.moduledoc = moduledoc
|
||||
|
||||
self.h1("%s plugin" % self.name) # : %s" %(self.name, self.oneliner))
|
||||
self.Print(self.oneliner)
|
||||
#self.h1("%s plugin" % self.name) # : %s" %(self.name, self.oneliner))
|
||||
self.h1(oneliner)
|
||||
#self.Print(self.oneliner)
|
||||
self.Print()
|
||||
self.Print(".. contents::")
|
||||
self.Print(" :local:")
|
||||
@@ -182,7 +188,8 @@ class PluginDoc(RestWriter):
|
||||
|
||||
self.emit_funcargs(plugin)
|
||||
self.emit_options(plugin)
|
||||
self.emit_source(plugin, config.hg_changeset)
|
||||
if name not in externals:
|
||||
self.emit_source(plugin, config.hg_changeset)
|
||||
#self.sourcelink = (purename,
|
||||
# "http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/" +
|
||||
# purename + ".py")
|
||||
@@ -212,12 +219,12 @@ class PluginDoc(RestWriter):
|
||||
# "py/test/plugin/%s" %(hg_changeset, basename)))
|
||||
self.links.append((basename,
|
||||
"http://bitbucket.org/hpk42/py-trunk/raw/%s/"
|
||||
"py/test/plugin/%s" %(pyversion, basename)))
|
||||
"py/_plugin/%s" %(pyversion, basename)))
|
||||
self.links.append(('customize', '../customize.html'))
|
||||
self.links.append(('plugins', 'index.html'))
|
||||
self.links.append(('get in contact', '../../contact.html'))
|
||||
self.links.append(('checkout the py.test development version',
|
||||
'../../download.html#checkout'))
|
||||
'../../install.html#checkout'))
|
||||
|
||||
if 0: # this breaks the page layout and makes large doc files
|
||||
#self.h2("plugin source code")
|
||||
@@ -252,7 +259,7 @@ class PluginDoc(RestWriter):
|
||||
warn("missing docstring", func)
|
||||
|
||||
def emit_options(self, plugin):
|
||||
from py.__.test.parseopt import Parser
|
||||
from py._test.parseopt import Parser
|
||||
options = []
|
||||
parser = Parser(processopt=options.append)
|
||||
if hasattr(plugin, 'pytest_addoption'):
|
||||
@@ -261,13 +268,16 @@ class PluginDoc(RestWriter):
|
||||
return
|
||||
self.h2("command line options")
|
||||
self.Print()
|
||||
formatter = py.compat.optparse.IndentedHelpFormatter()
|
||||
formatter = py.std.optparse.IndentedHelpFormatter()
|
||||
for opt in options:
|
||||
switches = formatter.format_option_strings(opt)
|
||||
self.Print("``%s``" % switches)
|
||||
self.Print(opt.help, indent=4)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if os.path.exists("py"):
|
||||
sys.path.insert(0, os.getcwd())
|
||||
import py
|
||||
_config = py.test.config
|
||||
_config.parse([])
|
||||
_config.pluginmanager.do_configure(_config)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import py
|
||||
import subprocess
|
||||
import os
|
||||
import os, sys
|
||||
|
||||
execnet = py.test.importorskip("execnet")
|
||||
|
||||
|
||||
#
|
||||
@@ -64,7 +66,10 @@ class VirtualEnv(object):
|
||||
return "<VirtualEnv at %r>" %(self.path)
|
||||
|
||||
def _cmd(self, name):
|
||||
return os.path.join(self.path, 'bin', name)
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(self.path, 'Scripts', name)
|
||||
else:
|
||||
return os.path.join(self.path, 'bin', name)
|
||||
|
||||
def ensure(self):
|
||||
if not os.path.exists(self._cmd('python')):
|
||||
@@ -78,7 +83,7 @@ class VirtualEnv(object):
|
||||
|
||||
def makegateway(self):
|
||||
python = self._cmd('python')
|
||||
return py.execnet.makegateway("popen//python=%s" %(python,))
|
||||
return execnet.makegateway("popen//python=%s" %(python,))
|
||||
|
||||
def pcall(self, cmd, *args, **kw):
|
||||
self.ensure()
|
||||
@@ -87,6 +92,16 @@ class VirtualEnv(object):
|
||||
] + list(args),
|
||||
**kw)
|
||||
|
||||
def pytest_getouterr(self, *args):
|
||||
self.ensure()
|
||||
args = [self._cmd("py.test")] + list(args)
|
||||
popen = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
out, err = popen.communicate()
|
||||
return out
|
||||
|
||||
def setup_develop(self):
|
||||
self.ensure()
|
||||
return self.pcall("python", "setup.py", "develop")
|
||||
|
||||
def easy_install(self, *packages, **kw):
|
||||
args = []
|
||||
@@ -109,3 +124,105 @@ def test_make_sdist_and_run_it(py_setup, venv):
|
||||
ch = gw.remote_exec("import py ; channel.send(py.__version__)")
|
||||
version = ch.receive()
|
||||
assert version == py.__version__
|
||||
|
||||
def test_plugin_setuptools_entry_point_integration(py_setup, venv, tmpdir):
|
||||
sdist = py_setup.make_sdist(venv.path)
|
||||
venv.easy_install(str(sdist))
|
||||
# create a sample plugin
|
||||
basedir = tmpdir.mkdir("testplugin")
|
||||
basedir.join("setup.py").write("""if 1:
|
||||
from setuptools import setup
|
||||
setup(name="testplugin",
|
||||
entry_points = {'pytest11': ['testplugin=tp1']},
|
||||
py_modules = ['tp1'],
|
||||
)
|
||||
""")
|
||||
basedir.join("tp1.py").write(py.code.Source("""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--testpluginopt", action="store_true")
|
||||
"""))
|
||||
basedir.chdir()
|
||||
print ("created sample plugin in %s" %basedir)
|
||||
venv.setup_develop()
|
||||
out = venv.pytest_getouterr("-h")
|
||||
assert "testpluginopt" in out
|
||||
|
||||
def test_cmdline_entrypoints(monkeypatch):
|
||||
monkeypatch.syspath_prepend(py.path.local(__file__).dirpath().dirpath())
|
||||
from setup import cmdline_entrypoints
|
||||
versioned_scripts = ['py.test', 'py.which']
|
||||
unversioned_scripts = versioned_scripts + [ 'py.cleanup',
|
||||
'py.convert_unittest', 'py.countloc', 'py.lookup', 'py.svnwcrevert']
|
||||
for ver in [(2,4,0), (2,5,0), (2,6,0), (2,7,0), (3,0,1), (3,1,1)]:
|
||||
for platform in ('posix', 'win32'):
|
||||
points = cmdline_entrypoints(ver, "posix", 'python')
|
||||
for script in versioned_scripts:
|
||||
script_ver = script + "-%s.%s" % ver[:2]
|
||||
assert script_ver in points
|
||||
for script in unversioned_scripts:
|
||||
assert script in points
|
||||
points = cmdline_entrypoints((2,5,1), "java1.6.123", 'jython')
|
||||
for script in versioned_scripts:
|
||||
expected = "%s-jython" % script
|
||||
assert expected in points
|
||||
for script in unversioned_scripts:
|
||||
assert script in points
|
||||
|
||||
points = cmdline_entrypoints((2,5,1), "xyz", 'pypy-c-XYZ')
|
||||
for script in versioned_scripts:
|
||||
expected = "%s-pypy-c-XYZ" % script
|
||||
assert expected in points
|
||||
for script in unversioned_scripts:
|
||||
assert script in points
|
||||
|
||||
def test_slave_popen_needs_no_pylib(testdir, venv, pytestconfig):
|
||||
pytestconfig.pluginmanager.skipifmissing("xdist")
|
||||
venv.ensure()
|
||||
#xxx execnet optimizes popen
|
||||
#ch = venv.makegateway().remote_exec("import execnet")
|
||||
#py.test.raises(ch.RemoteError, ch.waitclose)
|
||||
python = venv._cmd("python")
|
||||
p = testdir.makepyfile("""
|
||||
import py
|
||||
def test_func():
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest(p, '--rsyncdir=%s' % str(p),
|
||||
'--dist=each', '--tx=popen//python=%s' % python)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*1 passed*"
|
||||
])
|
||||
|
||||
def test_slave_needs_no_execnet(testdir, sshhost, pytestconfig):
|
||||
pytestconfig.pluginmanager.skipifmissing("xdist")
|
||||
xspec = "ssh=%s" % sshhost
|
||||
gw = execnet.makegateway("ssh=%s" % sshhost)
|
||||
ch = gw.remote_exec("""
|
||||
import os, subprocess
|
||||
subprocess.call(["virtualenv", "--no-site-packages", "subdir"])
|
||||
channel.send(os.path.join(os.path.abspath("subdir"), 'bin', 'python'))
|
||||
channel.send(os.path.join(os.path.abspath("subdir")))
|
||||
""")
|
||||
try:
|
||||
path = ch.receive()
|
||||
chdir = ch.receive()
|
||||
except ch.RemoteError:
|
||||
e = sys.exc_info()[1]
|
||||
py.test.skip("could not prepare ssh slave:%s" % str(e))
|
||||
gw.exit()
|
||||
newspec = "%s//python=%s//chdir=%s" % (xspec, path, chdir)
|
||||
gw = execnet.makegateway(newspec)
|
||||
ch = gw.remote_exec("import execnet")
|
||||
py.test.raises(ch.RemoteError, ch.waitclose)
|
||||
gw.exit()
|
||||
|
||||
p = testdir.makepyfile("""
|
||||
import py
|
||||
def test_func():
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest(p, '--rsyncdir=%s' % str(p),
|
||||
'--dist=each', '--tx=%s' % newspec)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*1 passed*"
|
||||
])
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# find and import a version of 'py'
|
||||
# find and import a version of 'py' that exists in a parent dir
|
||||
# of the current working directory. fall back to import a
|
||||
# globally available version
|
||||
#
|
||||
import sys
|
||||
import os
|
||||
@@ -19,7 +21,7 @@ def searchpy(current):
|
||||
# if p == current:
|
||||
# return True
|
||||
if current != sys.path[0]: # if we are already first, then ok
|
||||
print >>sys.stderr, "inserting into sys.path:", current
|
||||
sys.stderr.write("inserting into sys.path: %s\n" % current)
|
||||
sys.path.insert(0, current)
|
||||
return True
|
||||
current = opd(current)
|
||||
@@ -34,4 +36,4 @@ if not searchpy(abspath(os.curdir)):
|
||||
import py
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "py lib is at", py.__file__
|
||||
print ("py lib is at %s" % py.__file__)
|
||||
@@ -3,7 +3,7 @@
|
||||
import sys, os, os.path
|
||||
|
||||
progpath = sys.argv[0]
|
||||
packagedir = os.path.abspath(os.path.dirname(progpath))
|
||||
packagedir = os.path.dirname(os.path.dirname(os.path.abspath(progpath)))
|
||||
packagename = os.path.basename(packagedir)
|
||||
bindir = os.path.join(packagedir, 'bin')
|
||||
if sys.platform == 'win32':
|
||||
@@ -29,5 +29,5 @@ def setenv(name, value):
|
||||
assert False, 'Shell not supported.'
|
||||
return cmd
|
||||
|
||||
print prepend_path('PATH', bindir)
|
||||
print prepend_path('PYTHONPATH', rootdir)
|
||||
print(prepend_path('PATH', bindir))
|
||||
print(prepend_path('PYTHONPATH', rootdir))
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
from _findpy import py
|
||||
py.cmdline.pyrest()
|
||||
py.cmdline.pyconvert_unittest()
|
||||
2
bin/win32/py.convert_unittest.cmd
Normal file
2
bin/win32/py.convert_unittest.cmd
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
python "%~dp0\..\py.convert_unittest" %*
|
||||
95
conftest.py
Normal file
95
conftest.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import py
|
||||
import sys
|
||||
|
||||
pytest_plugins = '_pytest doctest pytester'.split()
|
||||
|
||||
collect_ignore = ['build', 'doc/_build']
|
||||
|
||||
rsyncdirs = ['conftest.py', 'bin', 'py', 'doc', 'testing']
|
||||
|
||||
import os, py
|
||||
pid = os.getpid()
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("pylib", "py lib testing options")
|
||||
group.addoption('--sshhost',
|
||||
action="store", dest="sshhost", default=None,
|
||||
help=("ssh xspec for ssh functional tests. "))
|
||||
group.addoption('--runslowtests',
|
||||
action="store_true", dest="runslowtests", default=False,
|
||||
help=("run slow tests"))
|
||||
group.addoption('--lsof',
|
||||
action="store_true", dest="lsof", default=False,
|
||||
help=("run FD checks if lsof is available"))
|
||||
|
||||
def pytest_configure(config):
|
||||
if config.getvalue("lsof"):
|
||||
try:
|
||||
out = py.process.cmdexec("lsof -p %d" % pid)
|
||||
except py.process.cmdexec.Error:
|
||||
pass
|
||||
else:
|
||||
config._numfiles = len([x for x in out.split("\n") if "REG" in x])
|
||||
|
||||
def pytest_unconfigure(config, __multicall__):
|
||||
if not hasattr(config, '_numfiles'):
|
||||
return
|
||||
__multicall__.execute()
|
||||
out2 = py.process.cmdexec("lsof -p %d" % pid)
|
||||
len2 = len([x for x in out2.split("\n") if "REG" in x])
|
||||
assert len2 < config._numfiles + 7, out2
|
||||
|
||||
def pytest_funcarg__sshhost(request):
|
||||
val = request.config.getvalue("sshhost")
|
||||
if val:
|
||||
return val
|
||||
py.test.skip("need --sshhost option")
|
||||
def pytest_generate_tests(metafunc):
|
||||
multi = getattr(metafunc.function, 'multi', None)
|
||||
if multi is not None:
|
||||
assert len(multi.kwargs) == 1
|
||||
for name, l in multi.kwargs.items():
|
||||
for val in l:
|
||||
metafunc.addcall(funcargs={name: val})
|
||||
elif 'anypython' in metafunc.funcargnames:
|
||||
for name in ('python2.4', 'python2.5', 'python2.6',
|
||||
'python2.7', 'python3.1', 'pypy-c', 'jython'):
|
||||
metafunc.addcall(id=name, param=name)
|
||||
|
||||
# XXX copied from execnet's conftest.py - needs to be merged
|
||||
winpymap = {
|
||||
'python2.7': r'C:\Python27\python.exe',
|
||||
'python2.6': r'C:\Python26\python.exe',
|
||||
'python2.5': r'C:\Python25\python.exe',
|
||||
'python2.4': r'C:\Python24\python.exe',
|
||||
'python3.1': r'C:\Python31\python.exe',
|
||||
}
|
||||
|
||||
def getexecutable(name, cache={}):
|
||||
try:
|
||||
return cache[name]
|
||||
except KeyError:
|
||||
executable = py.path.local.sysfind(name)
|
||||
if executable:
|
||||
if name == "jython":
|
||||
import subprocess
|
||||
popen = subprocess.Popen([str(executable), "--version"],
|
||||
universal_newlines=True, stderr=subprocess.PIPE)
|
||||
out, err = popen.communicate()
|
||||
if not err or "2.5" not in err:
|
||||
executable = None
|
||||
cache[name] = executable
|
||||
return executable
|
||||
|
||||
def pytest_funcarg__anypython(request):
|
||||
name = request.param
|
||||
executable = getexecutable(name)
|
||||
if executable is None:
|
||||
if sys.platform == "win32":
|
||||
executable = winpymap.get(name, None)
|
||||
if executable:
|
||||
executable = py.path.local(executable)
|
||||
if executable.check():
|
||||
return executable
|
||||
py.test.skip("no %s found" % (name,))
|
||||
return executable
|
||||
@@ -1,329 +0,0 @@
|
||||
"""
|
||||
Tested with coverage 2.85 and pygments 1.0
|
||||
|
||||
TODO:
|
||||
+ 'html-output/*,cover' should be deleted
|
||||
+ credits for coverage
|
||||
+ credits for pygments
|
||||
+ 'Install pygments' after ImportError is to less
|
||||
+ is the way of determining DIR_CSS_RESOURCE ok?
|
||||
+ write plugin test
|
||||
+ '.coverage' still exists in py.test execution dir
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import shutil
|
||||
from StringIO import StringIO
|
||||
|
||||
import py
|
||||
|
||||
try:
|
||||
from pygments import highlight
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
from pygments.formatters import HtmlFormatter
|
||||
except ImportError:
|
||||
print "Install pygments" # XXX
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
DIR_CUR = str(py.path.local())
|
||||
REPORT_FILE = os.path.join(DIR_CUR, '.coverage')
|
||||
DIR_ANNOTATE_OUTPUT = os.path.join(DIR_CUR, '.coverage_annotate')
|
||||
COVERAGE_MODULES = set()
|
||||
# coverage output parsing
|
||||
REG_COVERAGE_SUMMARY = re.compile('([a-z_\.]+) +([0-9]+) +([0-9]+) +([0-9]+%)')
|
||||
REG_COVERAGE_SUMMARY_TOTAL = re.compile('(TOTAL) +([0-9]+) +([0-9]+) +([0-9]+%)')
|
||||
DEFAULT_COVERAGE_OUTPUT = '.coverage_annotation'
|
||||
# HTML output specific
|
||||
DIR_CSS_RESOURCE = os.path.dirname(__import__('pytest_coverage').__file__)
|
||||
CSS_RESOURCE_FILES = ['header_bg.jpg', 'links.gif']
|
||||
|
||||
COVERAGE_TERM_HEADER = "\nCOVERAGE INFORMATION\n" \
|
||||
"====================\n"
|
||||
HTML_INDEX_HEADER = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>py.test - Coverage Index</title>
|
||||
|
||||
<style type="text/css">
|
||||
table {
|
||||
font-size:0.9em;
|
||||
font-family: Arial, Helvetica, verdana sans-serif;
|
||||
background-color:#fff;
|
||||
border-collapse: collapse;
|
||||
width: 500px;
|
||||
}
|
||||
caption {
|
||||
font-size: 25px;
|
||||
color: #1ba6b2;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
background: url(header_bg.jpg) no-repeat top left;
|
||||
padding: 10px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
thead th {
|
||||
border-right: 1px solid #fff;
|
||||
color:#fff;
|
||||
text-align:center;
|
||||
padding:2px;
|
||||
text-transform:uppercase;
|
||||
height:25px;
|
||||
background-color: #a3c159;
|
||||
font-weight: normal;
|
||||
}
|
||||
tfoot {
|
||||
color:#1ba6b2;
|
||||
padding:2px;
|
||||
text-transform:uppercase;
|
||||
font-size:1.2em;
|
||||
font-weigth: bold;
|
||||
margin-top:6px;
|
||||
border-top: 6px solid #e9f7f6;
|
||||
}
|
||||
tfoot td {
|
||||
text-align: left;
|
||||
}
|
||||
tbody tr {
|
||||
background-color:#fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
tbody td {
|
||||
color:#414141;
|
||||
padding:5px;
|
||||
text-align:left;
|
||||
}
|
||||
tbody th {
|
||||
text-align:left;
|
||||
padding:2px;
|
||||
}
|
||||
tbody td a, tbody th a {
|
||||
color:#6C8C37;
|
||||
text-decoration:none;
|
||||
font-weight:normal;
|
||||
display:block;
|
||||
background: transparent url(links.gif) no-repeat 0% 50%;
|
||||
padding-left:15px;
|
||||
}
|
||||
tbody td a:hover, tbody th a:hover {
|
||||
color:#009193;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body
|
||||
<table >
|
||||
<caption>Module Coverage</caption>
|
||||
<tbody>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Module</th>
|
||||
<th>Statements</th>
|
||||
<th>Executed</th>
|
||||
<th>Coverage</th>
|
||||
</tr>
|
||||
</thead>'''
|
||||
HTML_INDEX_FOOTER = ''' </tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>'''
|
||||
|
||||
|
||||
class CoverageHtmlFormatter(HtmlFormatter):
|
||||
"""XXX: doc"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
HtmlFormatter.__init__(self,*args, **kwargs)
|
||||
self.annotation_infos = kwargs.get('annotation_infos')
|
||||
|
||||
def _highlight_lines(self, tokensource):
|
||||
"""
|
||||
XXX: doc
|
||||
"""
|
||||
|
||||
hls = self.hl_lines
|
||||
self.annotation_infos = [None] + self.annotation_infos
|
||||
hls = [l for l, i in enumerate(self.annotation_infos) if i]
|
||||
for i, (t, value) in enumerate(tokensource):
|
||||
if t != 1:
|
||||
yield t, value
|
||||
if i + 1 in hls: # i + 1 because Python indexes start at 0
|
||||
if self.annotation_infos[i+1] == "!":
|
||||
yield 1, '<span style="background-color:#FFE5E5">%s</span>' \
|
||||
% value
|
||||
elif self.annotation_infos[i+1] == ">":
|
||||
yield 1, '<span style="background-color:#CCFFEB">%s</span>' \
|
||||
% value
|
||||
else:
|
||||
raise ValueError("HHAHA: %s" % self.annotation_infos[i+1])
|
||||
else:
|
||||
yield 1, value
|
||||
|
||||
|
||||
def _rename_annotation_files(module_list, dir_annotate_output):
|
||||
for m in module_list:
|
||||
mod_fpath = os.path.basename(m.__file__)
|
||||
if mod_fpath.endswith('pyc'):
|
||||
mod_fpath = mod_fpath[:-1]
|
||||
old = os.path.join(dir_annotate_output, '%s,cover'% mod_fpath)
|
||||
new = os.path.join(dir_annotate_output, '%s,cover'% m.__name__)
|
||||
if os.path.isfile(old):
|
||||
shutil.move(old, new)
|
||||
yield new
|
||||
|
||||
def _generate_module_coverage(mc_path, anotation_infos, src_lines):
|
||||
#XXX: doc
|
||||
|
||||
code = "".join(src_lines)
|
||||
mc_path = "%s.html" % mc_path
|
||||
lexer = get_lexer_by_name("python", stripall=True)
|
||||
formatter = CoverageHtmlFormatter(linenos=True, noclasses=True,
|
||||
hl_lines=[1], annotation_infos=anotation_infos)
|
||||
result = highlight(code, lexer, formatter)
|
||||
fp = open(mc_path, 'w')
|
||||
fp.write(result)
|
||||
fp.close()
|
||||
|
||||
def _parse_modulecoverage(mc_fpath):
|
||||
#XXX: doc
|
||||
|
||||
fd = open(mc_fpath, 'r')
|
||||
anotate_infos = []
|
||||
src_lines = []
|
||||
for line in fd.readlines():
|
||||
anotate_info = line[0:2].strip()
|
||||
if not anotate_info:
|
||||
anotate_info = None
|
||||
src_line = line[2:]
|
||||
anotate_infos.append(anotate_info)
|
||||
src_lines.append(src_line)
|
||||
return mc_fpath, anotate_infos, src_lines
|
||||
|
||||
def _parse_coverage_summary(fd):
|
||||
"""Parses coverage summary output."""
|
||||
|
||||
if hasattr(fd, 'readlines'):
|
||||
fd.seek(0)
|
||||
for l in fd.readlines():
|
||||
m = REG_COVERAGE_SUMMARY.match(l)
|
||||
if m:
|
||||
# yield name, stmts, execs, cover
|
||||
yield m.group(1), m.group(2), m.group(3), m.group(4)
|
||||
else:
|
||||
m = REG_COVERAGE_SUMMARY_TOTAL.match(l)
|
||||
if m:
|
||||
# yield name, stmts, execs, cover
|
||||
yield m.group(1), m.group(2), m.group(3), m.group(4)
|
||||
|
||||
|
||||
def _get_coverage_index(mod_name, stmts, execs, cover, annotation_dir):
|
||||
"""
|
||||
Generates the index page where are all modulare coverage reports are
|
||||
linked.
|
||||
"""
|
||||
|
||||
if mod_name == 'TOTAL':
|
||||
return '<tfoot><tr style="text-align: center;font-weigth:bold;font-size:1.2em; "><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr></tfoot>\n' % (mod_name, stmts, execs, cover)
|
||||
covrep_fpath = os.path.join(annotation_dir, '%s,cover.html' % mod_name)
|
||||
assert os.path.isfile(covrep_fpath) == True
|
||||
fname = os.path.basename(covrep_fpath)
|
||||
modlink = '<a href="%s">%s</a>' % (fname, mod_name)
|
||||
return '<tr style="text-align: center;"><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n' % (modlink, stmts, execs, cover)
|
||||
|
||||
|
||||
class CoveragePlugin:
|
||||
def pytest_addoption(self, parser):
|
||||
group = parser.addgroup('coverage options')
|
||||
group.addoption('-C', action='store_true', default=False,
|
||||
dest = 'coverage',
|
||||
help=('displays coverage information.'))
|
||||
group.addoption('--coverage-html', action='store', default=False,
|
||||
dest='coverage_annotation',
|
||||
help='path to the coverage HTML output dir.')
|
||||
group.addoption('--coverage-css-resourcesdir', action='store',
|
||||
default=DIR_CSS_RESOURCE,
|
||||
dest='coverage_css_ressourcedir',
|
||||
help='path to dir with css-resources (%s) for '
|
||||
'being copied to the HTML output dir.' % \
|
||||
", ".join(CSS_RESOURCE_FILES))
|
||||
|
||||
def pytest_configure(self, config):
|
||||
if config.getvalue('coverage'):
|
||||
try:
|
||||
import coverage
|
||||
except ImportError:
|
||||
raise config.Error("To run use the coverage option you have to install " \
|
||||
"Ned Batchelder's coverage: "\
|
||||
"http://nedbatchelder.com/code/modules/coverage.html")
|
||||
self.coverage = coverage
|
||||
self.summary = None
|
||||
|
||||
def pytest_terminal_summary(self, terminalreporter):
|
||||
if hasattr(self, 'coverage'):
|
||||
self.coverage.stop()
|
||||
module_list = [sys.modules[mod] for mod in COVERAGE_MODULES]
|
||||
module_list.sort()
|
||||
summary_fd = StringIO()
|
||||
# get coverage reports by module list
|
||||
self.coverage.report(module_list, file=summary_fd)
|
||||
summary = COVERAGE_TERM_HEADER + summary_fd.getvalue()
|
||||
terminalreporter._tw.write(summary)
|
||||
|
||||
config = terminalreporter.config
|
||||
dir_annotate_output = config.getvalue('coverage_annotation')
|
||||
if dir_annotate_output:
|
||||
if dir_annotate_output == "":
|
||||
dir_annotate_output = DIR_ANNOTATE_OUTPUT
|
||||
# create dir
|
||||
if os.path.isdir(dir_annotate_output):
|
||||
shutil.rmtree(dir_annotate_output)
|
||||
os.mkdir(dir_annotate_output)
|
||||
# generate annotation text files for later parsing
|
||||
self.coverage.annotate(module_list, dir_annotate_output)
|
||||
# generate the separate module coverage reports
|
||||
for mc_fpath in _rename_annotation_files(module_list, \
|
||||
dir_annotate_output):
|
||||
# mc_fpath, anotate_infos, src_lines from _parse_do
|
||||
_generate_module_coverage(*_parse_modulecoverage(mc_fpath))
|
||||
# creating contents for the index pagee for coverage report
|
||||
idxpage_html = StringIO()
|
||||
idxpage_html.write(HTML_INDEX_HEADER)
|
||||
total_sum = None
|
||||
for args in _parse_coverage_summary(summary_fd):
|
||||
# mod_name, stmts, execs, cover = args
|
||||
idxpage_html.write(_get_coverage_index(*args, \
|
||||
**dict(annotation_dir=dir_annotate_output)))
|
||||
idxpage_html.write(HTML_INDEX_FOOTER)
|
||||
idx_fpath = os.path.join(dir_annotate_output, 'index.html')
|
||||
idx_fd = open(idx_fpath, 'w')
|
||||
idx_fd.write(idxpage_html.getvalue())
|
||||
idx_fd.close()
|
||||
|
||||
dir_css_resource_dir = config.getvalue('coverage_css_ressourcedir')
|
||||
if dir_annotate_output and dir_css_resource_dir != "":
|
||||
if not os.path.isdir(dir_css_resource_dir):
|
||||
raise config.Error("CSS resource dir not found: '%s'" % \
|
||||
dir_css_resource_dir)
|
||||
for r in CSS_RESOURCE_FILES:
|
||||
src = os.path.join(dir_css_resource_dir, r)
|
||||
if os.path.isfile(src):
|
||||
dest = os.path.join(dir_annotate_output, r)
|
||||
shutil.copy(src, dest)
|
||||
|
||||
def pytest_collectstart(self, collector):
|
||||
if isinstance(collector, py.__.test.pycollect.Module):
|
||||
COVERAGE_MODULES.update(getattr(collector.obj,
|
||||
'COVERAGE_MODULES', []))
|
||||
|
||||
def pytest_testrunstart(self):
|
||||
print "self.coverage", self.coverage
|
||||
if hasattr(self, 'coverage'):
|
||||
print "START coverage"
|
||||
self.coverage.erase()
|
||||
self.coverage.start()
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 75 B |
20
contrib/runtesthelper.py
Normal file
20
contrib/runtesthelper.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
this little helper allows to run tests multiple times
|
||||
in the same process. useful for running tests from
|
||||
a console.
|
||||
|
||||
NOTE: since 1.3.1 you can just call py.test.cmdline.main()
|
||||
multiple times - no special logic needed.
|
||||
"""
|
||||
import py, sys
|
||||
|
||||
def pytest(argv=None):
|
||||
if argv is None:
|
||||
argv = []
|
||||
try:
|
||||
sys.argv[1:] = argv
|
||||
py.cmdline.pytest()
|
||||
except SystemExit:
|
||||
pass
|
||||
# we need to reset the global py.test.config object
|
||||
py.test.config = py.test.config.__class__()
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
|
||||
small utility for hot-syncing a svn repository through ssh.
|
||||
uses py.execnet.
|
||||
|
||||
"""
|
||||
|
||||
import py
|
||||
import sys, os
|
||||
|
||||
def usage():
|
||||
arg0 = sys.argv[0]
|
||||
print """%s [user@]remote-host:/repo/location localrepo [identity keyfile]""" % (arg0,)
|
||||
|
||||
|
||||
def main(args):
|
||||
remote = args[0]
|
||||
localrepo = py.path.local(args[1])
|
||||
if not localrepo.check(dir=1):
|
||||
raise SystemExit("localrepo %s does not exist" %(localrepo,))
|
||||
if len(args) == 3:
|
||||
keyfile = py.path.local(args[2])
|
||||
else:
|
||||
keyfile = None
|
||||
remote_host, path = remote.split(':', 1)
|
||||
print "ssh-connecting to", remote_host
|
||||
gw = getgateway(remote_host, keyfile)
|
||||
|
||||
local_rev = get_svn_youngest(localrepo)
|
||||
|
||||
# local protocol
|
||||
# 1. client sends rev/repo -> server
|
||||
# 2. server checks for newer revisions and sends dumps
|
||||
# 3. client receives dumps, updates local repo
|
||||
# 4. client goes back to step 1
|
||||
c = gw.remote_exec("""
|
||||
import py
|
||||
import os
|
||||
remote_rev, repopath = channel.receive()
|
||||
while 1:
|
||||
rev = py.process.cmdexec('svnlook youngest "%s"' % repopath)
|
||||
rev = int(rev)
|
||||
if rev > remote_rev:
|
||||
revrange = (remote_rev+1, rev)
|
||||
dumpchannel = channel.gateway.newchannel()
|
||||
channel.send(revrange)
|
||||
channel.send(dumpchannel)
|
||||
|
||||
f = os.popen(
|
||||
"svnadmin dump -q --incremental -r %s:%s %s"
|
||||
% (revrange[0], revrange[1], repopath), 'r')
|
||||
try:
|
||||
maxcount = dumpchannel.receive()
|
||||
count = maxcount
|
||||
while 1:
|
||||
s = f.read(8192)
|
||||
if not s:
|
||||
raise EOFError
|
||||
dumpchannel.send(s)
|
||||
count = count - 1
|
||||
if count <= 0:
|
||||
ack = dumpchannel.receive()
|
||||
count = maxcount
|
||||
|
||||
except EOFError:
|
||||
dumpchannel.close()
|
||||
remote_rev = rev
|
||||
else:
|
||||
# using svn-hook instead would be nice here
|
||||
py.std.time.sleep(30)
|
||||
""")
|
||||
|
||||
c.send((local_rev, path))
|
||||
print "checking revisions from %d in %s" %(local_rev, remote)
|
||||
while 1:
|
||||
revstart, revend = c.receive()
|
||||
dumpchannel = c.receive()
|
||||
print "receiving revisions", revstart, "-", revend, "replaying..."
|
||||
svn_load(localrepo, dumpchannel)
|
||||
print "current revision", revend
|
||||
|
||||
def svn_load(repo, dumpchannel, maxcount=100):
|
||||
# every maxcount we will send an ACK to the other
|
||||
# side in order to synchronise and avoid our side
|
||||
# growing buffers (py.execnet does not control
|
||||
# RAM usage or receive queue sizes)
|
||||
dumpchannel.send(maxcount)
|
||||
f = os.popen("svnadmin load -q %s" %(repo, ), "w")
|
||||
count = maxcount
|
||||
for x in dumpchannel:
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
f.write(x)
|
||||
count = count - 1
|
||||
if count <= 0:
|
||||
dumpchannel.send(maxcount)
|
||||
count = maxcount
|
||||
print >>sys.stdout
|
||||
f.close()
|
||||
|
||||
def get_svn_youngest(repo):
|
||||
rev = py.process.cmdexec('svnlook youngest "%s"' % repo)
|
||||
return int(rev)
|
||||
|
||||
def getgateway(host, keyfile=None):
|
||||
return py.execnet.SshGateway(host, identity=keyfile)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
usage()
|
||||
raise SystemExit(1)
|
||||
|
||||
main(sys.argv[1:])
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
"""
|
||||
sysinfo.py [host1] [host2] [options]
|
||||
|
||||
obtain system info from remote machine.
|
||||
"""
|
||||
|
||||
import py
|
||||
import sys
|
||||
|
||||
optparse = py.compat.optparse
|
||||
|
||||
parser = optparse.OptionParser(usage=__doc__)
|
||||
parser.add_option("-f", "--sshconfig", action="store", dest="ssh_config", default=None,
|
||||
help="use given ssh config file, and add info all contained hosts for getting info")
|
||||
parser.add_option("-i", "--ignore", action="store", dest="ignores", default=None,
|
||||
help="ignore hosts (useful if the list of hostnames come from a file list)")
|
||||
|
||||
def parsehosts(path):
|
||||
path = py.path.local(path)
|
||||
l = []
|
||||
rex = py.std.re.compile(r'Host\s*(\S+)')
|
||||
for line in path.readlines():
|
||||
m = rex.match(line)
|
||||
if m is not None:
|
||||
sshname, = m.groups()
|
||||
l.append(sshname)
|
||||
return l
|
||||
|
||||
class RemoteInfo:
|
||||
def __init__(self, gateway):
|
||||
self.gw = gateway
|
||||
self._cache = {}
|
||||
|
||||
def exreceive(self, execstring):
|
||||
if execstring not in self._cache:
|
||||
channel = self.gw.remote_exec(execstring)
|
||||
self._cache[execstring] = channel.receive()
|
||||
return self._cache[execstring]
|
||||
|
||||
def getmodattr(self, modpath):
|
||||
module = modpath.split(".")[0]
|
||||
return self.exreceive("""
|
||||
import %s
|
||||
channel.send(%s)
|
||||
""" %(module, modpath))
|
||||
|
||||
def islinux(self):
|
||||
return self.getmodattr('sys.platform').find("linux") != -1
|
||||
|
||||
def getfqdn(self):
|
||||
return self.exreceive("""
|
||||
import socket
|
||||
channel.send(socket.getfqdn())
|
||||
""")
|
||||
|
||||
def getmemswap(self):
|
||||
if self.islinux():
|
||||
return self.exreceive("""
|
||||
import commands, re
|
||||
out = commands.getoutput("free")
|
||||
mem = re.search(r"Mem:\s+(\S*)", out).group(1)
|
||||
swap = re.search(r"Swap:\s+(\S*)", out).group(1)
|
||||
channel.send((mem, swap))
|
||||
""")
|
||||
|
||||
def getcpuinfo(self):
|
||||
if self.islinux():
|
||||
return self.exreceive("""
|
||||
# a hyperthreaded cpu core only counts as 1, although it
|
||||
# is present as 2 in /proc/cpuinfo. Counting it as 2 is
|
||||
# misleading because it is *by far* not as efficient as
|
||||
# two independent cores.
|
||||
cpus = {}
|
||||
cpuinfo = {}
|
||||
f = open("/proc/cpuinfo")
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
for line in lines + ['']:
|
||||
if line.strip():
|
||||
key, value = line.split(":", 1)
|
||||
cpuinfo[key.strip()] = value.strip()
|
||||
else:
|
||||
corekey = (cpuinfo.get("physical id"),
|
||||
cpuinfo.get("core id"))
|
||||
cpus[corekey] = 1
|
||||
numcpus = len(cpus)
|
||||
model = cpuinfo.get("model name")
|
||||
channel.send((numcpus, model))
|
||||
""")
|
||||
|
||||
def debug(*args):
|
||||
print >>sys.stderr, " ".join(map(str, args))
|
||||
def error(*args):
|
||||
debug("ERROR", args[0] + ":", *args[1:])
|
||||
|
||||
def getinfo(sshname, ssh_config=None, loginfo=sys.stdout):
|
||||
debug("connecting to", sshname)
|
||||
try:
|
||||
gw = py.execnet.SshGateway(sshname, ssh_config=ssh_config)
|
||||
except IOError:
|
||||
error("could not get sshagteway", sshname)
|
||||
else:
|
||||
ri = RemoteInfo(gw)
|
||||
#print "%s info:" % sshname
|
||||
prefix = sshname.upper() + " "
|
||||
print >>loginfo, prefix, "fqdn:", ri.getfqdn()
|
||||
for attr in (
|
||||
"sys.platform",
|
||||
"sys.version_info",
|
||||
):
|
||||
loginfo.write("%s %s: " %(prefix, attr,))
|
||||
loginfo.flush()
|
||||
value = ri.getmodattr(attr)
|
||||
loginfo.write(str(value))
|
||||
loginfo.write("\n")
|
||||
loginfo.flush()
|
||||
memswap = ri.getmemswap()
|
||||
if memswap:
|
||||
mem,swap = memswap
|
||||
print >>loginfo, prefix, "Memory:", mem, "Swap:", swap
|
||||
cpuinfo = ri.getcpuinfo()
|
||||
if cpuinfo:
|
||||
numcpu, model = cpuinfo
|
||||
print >>loginfo, prefix, "number of cpus:", numcpu
|
||||
print >>loginfo, prefix, "cpu model", model
|
||||
return ri
|
||||
|
||||
if __name__ == '__main__':
|
||||
options, args = parser.parse_args()
|
||||
hosts = list(args)
|
||||
ssh_config = options.ssh_config
|
||||
if ssh_config:
|
||||
hosts.extend(parsehosts(ssh_config))
|
||||
ignores = options.ignores or ()
|
||||
if ignores:
|
||||
ignores = ignores.split(",")
|
||||
for host in hosts:
|
||||
if host not in ignores:
|
||||
getinfo(host, ssh_config=ssh_config)
|
||||
|
||||
485
distribute_setup.py
Normal file
485
distribute_setup.py
Normal file
@@ -0,0 +1,485 @@
|
||||
#!python
|
||||
"""Bootstrap distribute installation
|
||||
|
||||
If you want to use setuptools in your package's setup.py, just include this
|
||||
file in the same directory with it, and add this to the top of your setup.py::
|
||||
|
||||
from distribute_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
If you want to require a specific version of setuptools, set a download
|
||||
mirror, or use an alternate download directory, you can do so by supplying
|
||||
the appropriate options to ``use_setuptools()``.
|
||||
|
||||
This file can also be run as a script to install or upgrade setuptools.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import fnmatch
|
||||
import tempfile
|
||||
import tarfile
|
||||
from distutils import log
|
||||
|
||||
try:
|
||||
from site import USER_SITE
|
||||
except ImportError:
|
||||
USER_SITE = None
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
def _python_cmd(*args):
|
||||
args = (sys.executable,) + args
|
||||
return subprocess.call(args) == 0
|
||||
|
||||
except ImportError:
|
||||
# will be used for python 2.3
|
||||
def _python_cmd(*args):
|
||||
args = (sys.executable,) + args
|
||||
# quoting arguments if windows
|
||||
if sys.platform == 'win32':
|
||||
def quote(arg):
|
||||
if ' ' in arg:
|
||||
return '"%s"' % arg
|
||||
return arg
|
||||
args = [quote(arg) for arg in args]
|
||||
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
|
||||
|
||||
DEFAULT_VERSION = "0.6.13"
|
||||
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
|
||||
SETUPTOOLS_FAKED_VERSION = "0.6c11"
|
||||
|
||||
SETUPTOOLS_PKG_INFO = """\
|
||||
Metadata-Version: 1.0
|
||||
Name: setuptools
|
||||
Version: %s
|
||||
Summary: xxxx
|
||||
Home-page: xxx
|
||||
Author: xxx
|
||||
Author-email: xxx
|
||||
License: xxx
|
||||
Description: xxx
|
||||
""" % SETUPTOOLS_FAKED_VERSION
|
||||
|
||||
|
||||
def _install(tarball):
|
||||
# extracting the tarball
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
tar = tarfile.open(tarball)
|
||||
_extractall(tar)
|
||||
tar.close()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
|
||||
# installing
|
||||
log.warn('Installing Distribute')
|
||||
if not _python_cmd('setup.py', 'install'):
|
||||
log.warn('Something went wrong during the installation.')
|
||||
log.warn('See the error message above.')
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
|
||||
|
||||
def _build_egg(egg, tarball, to_dir):
|
||||
# extracting the tarball
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
tar = tarfile.open(tarball)
|
||||
_extractall(tar)
|
||||
tar.close()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
|
||||
# building an egg
|
||||
log.warn('Building a Distribute egg in %s', to_dir)
|
||||
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
# returning the result
|
||||
log.warn(egg)
|
||||
if not os.path.exists(egg):
|
||||
raise IOError('Could not build the egg.')
|
||||
|
||||
|
||||
def _do_download(version, download_base, to_dir, download_delay):
|
||||
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
|
||||
% (version, sys.version_info[0], sys.version_info[1]))
|
||||
if not os.path.exists(egg):
|
||||
tarball = download_setuptools(version, download_base,
|
||||
to_dir, download_delay)
|
||||
_build_egg(egg, tarball, to_dir)
|
||||
sys.path.insert(0, egg)
|
||||
import setuptools
|
||||
setuptools.bootstrap_install_from = egg
|
||||
|
||||
|
||||
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, download_delay=15, no_fake=True):
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
was_imported = 'pkg_resources' in sys.modules or \
|
||||
'setuptools' in sys.modules
|
||||
try:
|
||||
try:
|
||||
import pkg_resources
|
||||
if not hasattr(pkg_resources, '_distribute'):
|
||||
if not no_fake:
|
||||
_fake_setuptools()
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
try:
|
||||
pkg_resources.require("distribute>="+version)
|
||||
return
|
||||
except pkg_resources.VersionConflict:
|
||||
e = sys.exc_info()[1]
|
||||
if was_imported:
|
||||
sys.stderr.write(
|
||||
"The required version of distribute (>=%s) is not available,\n"
|
||||
"and can't be installed while this script is running. Please\n"
|
||||
"install a more recent version first, using\n"
|
||||
"'easy_install -U distribute'."
|
||||
"\n\n(Currently using %r)\n" % (version, e.args[0]))
|
||||
sys.exit(2)
|
||||
else:
|
||||
del pkg_resources, sys.modules['pkg_resources'] # reload ok
|
||||
return _do_download(version, download_base, to_dir,
|
||||
download_delay)
|
||||
except pkg_resources.DistributionNotFound:
|
||||
return _do_download(version, download_base, to_dir,
|
||||
download_delay)
|
||||
finally:
|
||||
if not no_fake:
|
||||
_create_fake_setuptools_pkg_info(to_dir)
|
||||
|
||||
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, delay=15):
|
||||
"""Download distribute from a specified location and return its filename
|
||||
|
||||
`version` should be a valid distribute version number that is available
|
||||
as an egg for download under the `download_base` URL (which should end
|
||||
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||
`delay` is the number of seconds to pause before an actual download
|
||||
attempt.
|
||||
"""
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
from urllib2 import urlopen
|
||||
tgz_name = "distribute-%s.tar.gz" % version
|
||||
url = download_base + tgz_name
|
||||
saveto = os.path.join(to_dir, tgz_name)
|
||||
src = dst = None
|
||||
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||
try:
|
||||
log.warn("Downloading %s", url)
|
||||
src = urlopen(url)
|
||||
# Read/write all in one block, so we don't create a corrupt file
|
||||
# if the download is interrupted.
|
||||
data = src.read()
|
||||
dst = open(saveto, "wb")
|
||||
dst.write(data)
|
||||
finally:
|
||||
if src:
|
||||
src.close()
|
||||
if dst:
|
||||
dst.close()
|
||||
return os.path.realpath(saveto)
|
||||
|
||||
def _no_sandbox(function):
|
||||
def __no_sandbox(*args, **kw):
|
||||
try:
|
||||
from setuptools.sandbox import DirectorySandbox
|
||||
if not hasattr(DirectorySandbox, '_old'):
|
||||
def violation(*args):
|
||||
pass
|
||||
DirectorySandbox._old = DirectorySandbox._violation
|
||||
DirectorySandbox._violation = violation
|
||||
patched = True
|
||||
else:
|
||||
patched = False
|
||||
except ImportError:
|
||||
patched = False
|
||||
|
||||
try:
|
||||
return function(*args, **kw)
|
||||
finally:
|
||||
if patched:
|
||||
DirectorySandbox._violation = DirectorySandbox._old
|
||||
del DirectorySandbox._old
|
||||
|
||||
return __no_sandbox
|
||||
|
||||
def _patch_file(path, content):
|
||||
"""Will backup the file then patch it"""
|
||||
existing_content = open(path).read()
|
||||
if existing_content == content:
|
||||
# already patched
|
||||
log.warn('Already patched.')
|
||||
return False
|
||||
log.warn('Patching...')
|
||||
_rename_path(path)
|
||||
f = open(path, 'w')
|
||||
try:
|
||||
f.write(content)
|
||||
finally:
|
||||
f.close()
|
||||
return True
|
||||
|
||||
_patch_file = _no_sandbox(_patch_file)
|
||||
|
||||
def _same_content(path, content):
|
||||
return open(path).read() == content
|
||||
|
||||
def _rename_path(path):
|
||||
new_name = path + '.OLD.%s' % time.time()
|
||||
log.warn('Renaming %s into %s', path, new_name)
|
||||
os.rename(path, new_name)
|
||||
return new_name
|
||||
|
||||
def _remove_flat_installation(placeholder):
|
||||
if not os.path.isdir(placeholder):
|
||||
log.warn('Unkown installation at %s', placeholder)
|
||||
return False
|
||||
found = False
|
||||
for file in os.listdir(placeholder):
|
||||
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
log.warn('Could not locate setuptools*.egg-info')
|
||||
return
|
||||
|
||||
log.warn('Removing elements out of the way...')
|
||||
pkg_info = os.path.join(placeholder, file)
|
||||
if os.path.isdir(pkg_info):
|
||||
patched = _patch_egg_dir(pkg_info)
|
||||
else:
|
||||
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
|
||||
|
||||
if not patched:
|
||||
log.warn('%s already patched.', pkg_info)
|
||||
return False
|
||||
# now let's move the files out of the way
|
||||
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
|
||||
element = os.path.join(placeholder, element)
|
||||
if os.path.exists(element):
|
||||
_rename_path(element)
|
||||
else:
|
||||
log.warn('Could not find the %s element of the '
|
||||
'Setuptools distribution', element)
|
||||
return True
|
||||
|
||||
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
|
||||
|
||||
def _after_install(dist):
|
||||
log.warn('After install bootstrap.')
|
||||
placeholder = dist.get_command_obj('install').install_purelib
|
||||
_create_fake_setuptools_pkg_info(placeholder)
|
||||
|
||||
def _create_fake_setuptools_pkg_info(placeholder):
|
||||
if not placeholder or not os.path.exists(placeholder):
|
||||
log.warn('Could not find the install location')
|
||||
return
|
||||
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
|
||||
(SETUPTOOLS_FAKED_VERSION, pyver)
|
||||
pkg_info = os.path.join(placeholder, setuptools_file)
|
||||
if os.path.exists(pkg_info):
|
||||
log.warn('%s already exists', pkg_info)
|
||||
return
|
||||
|
||||
log.warn('Creating %s', pkg_info)
|
||||
f = open(pkg_info, 'w')
|
||||
try:
|
||||
f.write(SETUPTOOLS_PKG_INFO)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
pth_file = os.path.join(placeholder, 'setuptools.pth')
|
||||
log.warn('Creating %s', pth_file)
|
||||
f = open(pth_file, 'w')
|
||||
try:
|
||||
f.write(os.path.join(os.curdir, setuptools_file))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
|
||||
|
||||
def _patch_egg_dir(path):
|
||||
# let's check if it's already patched
|
||||
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||
if os.path.exists(pkg_info):
|
||||
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
|
||||
log.warn('%s already patched.', pkg_info)
|
||||
return False
|
||||
_rename_path(path)
|
||||
os.mkdir(path)
|
||||
os.mkdir(os.path.join(path, 'EGG-INFO'))
|
||||
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||
f = open(pkg_info, 'w')
|
||||
try:
|
||||
f.write(SETUPTOOLS_PKG_INFO)
|
||||
finally:
|
||||
f.close()
|
||||
return True
|
||||
|
||||
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
|
||||
|
||||
def _before_install():
|
||||
log.warn('Before install bootstrap.')
|
||||
_fake_setuptools()
|
||||
|
||||
|
||||
def _under_prefix(location):
|
||||
if 'install' not in sys.argv:
|
||||
return True
|
||||
args = sys.argv[sys.argv.index('install')+1:]
|
||||
for index, arg in enumerate(args):
|
||||
for option in ('--root', '--prefix'):
|
||||
if arg.startswith('%s=' % option):
|
||||
top_dir = arg.split('root=')[-1]
|
||||
return location.startswith(top_dir)
|
||||
elif arg == option:
|
||||
if len(args) > index:
|
||||
top_dir = args[index+1]
|
||||
return location.startswith(top_dir)
|
||||
if arg == '--user' and USER_SITE is not None:
|
||||
return location.startswith(USER_SITE)
|
||||
return True
|
||||
|
||||
|
||||
def _fake_setuptools():
|
||||
log.warn('Scanning installed packages')
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
# we're cool
|
||||
log.warn('Setuptools or Distribute does not seem to be installed.')
|
||||
return
|
||||
ws = pkg_resources.working_set
|
||||
try:
|
||||
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
|
||||
replacement=False))
|
||||
except TypeError:
|
||||
# old distribute API
|
||||
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
|
||||
|
||||
if setuptools_dist is None:
|
||||
log.warn('No setuptools distribution found')
|
||||
return
|
||||
# detecting if it was already faked
|
||||
setuptools_location = setuptools_dist.location
|
||||
log.warn('Setuptools installation detected at %s', setuptools_location)
|
||||
|
||||
# if --root or --preix was provided, and if
|
||||
# setuptools is not located in them, we don't patch it
|
||||
if not _under_prefix(setuptools_location):
|
||||
log.warn('Not patching, --root or --prefix is installing Distribute'
|
||||
' in another location')
|
||||
return
|
||||
|
||||
# let's see if its an egg
|
||||
if not setuptools_location.endswith('.egg'):
|
||||
log.warn('Non-egg installation')
|
||||
res = _remove_flat_installation(setuptools_location)
|
||||
if not res:
|
||||
return
|
||||
else:
|
||||
log.warn('Egg installation')
|
||||
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
|
||||
if (os.path.exists(pkg_info) and
|
||||
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
|
||||
log.warn('Already patched.')
|
||||
return
|
||||
log.warn('Patching...')
|
||||
# let's create a fake egg replacing setuptools one
|
||||
res = _patch_egg_dir(setuptools_location)
|
||||
if not res:
|
||||
return
|
||||
log.warn('Patched done.')
|
||||
_relaunch()
|
||||
|
||||
|
||||
def _relaunch():
|
||||
log.warn('Relaunching...')
|
||||
# we have to relaunch the process
|
||||
# pip marker to avoid a relaunch bug
|
||||
if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
|
||||
sys.argv[0] = 'setup.py'
|
||||
args = [sys.executable] + sys.argv
|
||||
sys.exit(subprocess.call(args))
|
||||
|
||||
|
||||
def _extractall(self, path=".", members=None):
|
||||
"""Extract all members from the archive to the current working
|
||||
directory and set owner, modification time and permissions on
|
||||
directories afterwards. `path' specifies a different directory
|
||||
to extract to. `members' is optional and must be a subset of the
|
||||
list returned by getmembers().
|
||||
"""
|
||||
import copy
|
||||
import operator
|
||||
from tarfile import ExtractError
|
||||
directories = []
|
||||
|
||||
if members is None:
|
||||
members = self
|
||||
|
||||
for tarinfo in members:
|
||||
if tarinfo.isdir():
|
||||
# Extract directories with a safe mode.
|
||||
directories.append(tarinfo)
|
||||
tarinfo = copy.copy(tarinfo)
|
||||
tarinfo.mode = 448 # decimal for oct 0700
|
||||
self.extract(tarinfo, path)
|
||||
|
||||
# Reverse sort directories.
|
||||
if sys.version_info < (2, 4):
|
||||
def sorter(dir1, dir2):
|
||||
return cmp(dir1.name, dir2.name)
|
||||
directories.sort(sorter)
|
||||
directories.reverse()
|
||||
else:
|
||||
directories.sort(key=operator.attrgetter('name'), reverse=True)
|
||||
|
||||
# Set correct owner, mtime and filemode on directories.
|
||||
for tarinfo in directories:
|
||||
dirpath = os.path.join(path, tarinfo.name)
|
||||
try:
|
||||
self.chown(tarinfo, dirpath)
|
||||
self.utime(tarinfo, dirpath)
|
||||
self.chmod(tarinfo, dirpath)
|
||||
except ExtractError:
|
||||
e = sys.exc_info()[1]
|
||||
if self.errorlevel > 1:
|
||||
raise
|
||||
else:
|
||||
self._dbg(1, "tarfile: %s" % e)
|
||||
|
||||
|
||||
def main(argv, version=DEFAULT_VERSION):
|
||||
"""Install or upgrade setuptools and EasyInstall"""
|
||||
tarball = download_setuptools()
|
||||
_install(tarball)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
115
doc/announce/release-1.1.0.txt
Normal file
115
doc/announce/release-1.1.0.txt
Normal file
@@ -0,0 +1,115 @@
|
||||
py.test/pylib 1.1.0: Python3, Jython, advanced skipping, cleanups ...
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Features:
|
||||
|
||||
* compatible to Python3 (single py2/py3 source), `easy to install`_
|
||||
* conditional skipping_: skip/xfail based on platform/dependencies
|
||||
* generalized marking_: mark tests one a whole-class or whole-module basis
|
||||
|
||||
Fixes:
|
||||
|
||||
* code reduction and "de-magification" (e.g. 23 KLoc -> 11 KLOC)
|
||||
* distribute testing requires the now separately released execnet_ package
|
||||
* funcarg-setup/caching, "same-name" test modules now cause an exlicit error
|
||||
* de-cluttered reporting options, --report for skipped/xfail details
|
||||
|
||||
Compatibilities
|
||||
|
||||
1.1.0 should allow running test code that already worked well with 1.0.2
|
||||
plus some more due to improved unittest/nose compatibility.
|
||||
|
||||
More information: http://pytest.org
|
||||
|
||||
thanks and have fun,
|
||||
|
||||
holger (http://twitter.com/hpk42)
|
||||
|
||||
.. _execnet: http://codespeak.net/execnet
|
||||
.. _`easy to install`: ../install.html
|
||||
.. _marking: ../test/plugin/mark.html
|
||||
.. _skipping: ../test/plugin/skipping.html
|
||||
|
||||
|
||||
Changelog 1.0.2 -> 1.1.0
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
* remove py.rest tool and internal namespace - it was
|
||||
never really advertised and can still be used with
|
||||
the old release if needed. If there is interest
|
||||
it could be revived into its own tool i guess.
|
||||
|
||||
* fix issue48 and issue59: raise an Error if the module
|
||||
from an imported test file does not seem to come from
|
||||
the filepath - avoids "same-name" confusion that has
|
||||
been reported repeatedly
|
||||
|
||||
* merged Ronny's nose-compatibility hacks: now
|
||||
nose-style setup_module() and setup() functions are
|
||||
supported
|
||||
|
||||
* introduce generalized py.test.mark function marking
|
||||
|
||||
* reshuffle / refine command line grouping
|
||||
|
||||
* deprecate parser.addgroup in favour of getgroup which creates option group
|
||||
|
||||
* add --report command line option that allows to control showing of skipped/xfailed sections
|
||||
|
||||
* generalized skipping: a new way to mark python functions with skipif or xfail
|
||||
at function, class and modules level based on platform or sys-module attributes.
|
||||
|
||||
* extend py.test.mark decorator to allow for positional args
|
||||
|
||||
* introduce and test "py.cleanup -d" to remove empty directories
|
||||
|
||||
* fix issue #59 - robustify unittest test collection
|
||||
|
||||
* make bpython/help interaction work by adding an __all__ attribute
|
||||
to ApiModule, cleanup initpkg
|
||||
|
||||
* use MIT license for pylib, add some contributors
|
||||
|
||||
* remove py.execnet code and substitute all usages with 'execnet' proper
|
||||
|
||||
* fix issue50 - cached_setup now caches more to expectations
|
||||
for test functions with multiple arguments.
|
||||
|
||||
* merge Jarko's fixes, issue #45 and #46
|
||||
|
||||
* add the ability to specify a path for py.lookup to search in
|
||||
|
||||
* fix a funcarg cached_setup bug probably only occuring
|
||||
in distributed testing and "module" scope with teardown.
|
||||
|
||||
* many fixes and changes for making the code base python3 compatible,
|
||||
many thanks to Benjamin Peterson for helping with this.
|
||||
|
||||
* consolidate builtins implementation to be compatible with >=2.3,
|
||||
add helpers to ease keeping 2 and 3k compatible code
|
||||
|
||||
* deprecate py.compat.doctest|subprocess|textwrap|optparse
|
||||
|
||||
* deprecate py.magic.autopath, remove py/magic directory
|
||||
|
||||
* move pytest assertion handling to py/code and a pytest_assertion
|
||||
plugin, add "--no-assert" option, deprecate py.magic namespaces
|
||||
in favour of (less) py.code ones.
|
||||
|
||||
* consolidate and cleanup py/code classes and files
|
||||
|
||||
* cleanup py/misc, move tests to bin-for-dist
|
||||
|
||||
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
|
||||
|
||||
* consolidate py.log implementation, remove old approach.
|
||||
|
||||
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
||||
text/unicode and byte-streams (uses underlying standard lib io.*
|
||||
if available)
|
||||
|
||||
* make py.unittest_convert helper script available which converts "unittest.py"
|
||||
style files into the simpler assert/direct-test-classes py.test/nosetests
|
||||
style. The script was written by Laura Creighton.
|
||||
|
||||
* simplified internal localpath implementation
|
||||
48
doc/announce/release-1.1.1.txt
Normal file
48
doc/announce/release-1.1.1.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
py.test/pylib 1.1.1: bugfix release, setuptools plugin registration
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This is a compatibility fixing release of pylib/py.test to work
|
||||
better with previous 1.0.x test code bases. It also contains fixes
|
||||
and changes to work with `execnet>=1.0.0`_ to provide distributed
|
||||
testing and looponfailing testing modes. py-1.1.1 also introduces
|
||||
a new mechanism for registering plugins via setuptools.
|
||||
|
||||
What is pylib/py.test?
|
||||
-----------------------
|
||||
|
||||
py.test is an advanced automated testing tool working with
|
||||
Python2, Python3 and Jython versions on all major operating
|
||||
systems. It has an extensive plugin architecture and can run many
|
||||
existing common Python test suites without modification. Moreover,
|
||||
it offers some unique features not found in other
|
||||
testing tools. See http://pytest.org for more info.
|
||||
|
||||
The pylib also contains a localpath and svnpath implementation
|
||||
and some developer-oriented command line tools. See
|
||||
http://pylib.org for more info.
|
||||
|
||||
thanks to all who helped and gave feedback,
|
||||
have fun,
|
||||
|
||||
holger (http://twitter.com/hpk42)
|
||||
|
||||
.. _`execnet>=1.0.0`: http://codespeak.net/execnet
|
||||
|
||||
Changes between 1.1.1 and 1.1.0
|
||||
=====================================
|
||||
|
||||
- introduce automatic plugin registration via 'pytest11'
|
||||
entrypoints via setuptools' pkg_resources.iter_entry_points
|
||||
|
||||
- fix py.test dist-testing to work with execnet >= 1.0.0b4
|
||||
|
||||
- re-introduce py.test.cmdline.main() for better backward compatibility
|
||||
|
||||
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
|
||||
allow '%' in svn paths, make svnwc.update() default to interactive mode
|
||||
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
|
||||
|
||||
- refine distributed tarball to contain test and no pyc files
|
||||
|
||||
- try harder to have deprecation warnings for py.compat.* accesses
|
||||
report a correct location
|
||||
116
doc/announce/release-1.2.0.txt
Normal file
116
doc/announce/release-1.2.0.txt
Normal file
@@ -0,0 +1,116 @@
|
||||
py.test/pylib 1.2.0: junitxml, standalone test scripts, pluginization
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
py.test is an advanced automated testing tool working with
|
||||
Python2, Python3 and Jython versions on all major operating
|
||||
systems. It has a simple plugin architecture and can run many
|
||||
existing common Python test suites without modification. It offers
|
||||
some unique features not found in other testing tools.
|
||||
See http://pytest.org for more info.
|
||||
|
||||
py.test 1.2.0 brings many bug fixes and interesting new abilities:
|
||||
|
||||
* --junitxml=path will create an XML file for use with CI processing
|
||||
* --genscript=path creates a standalone py.test-equivalent test-script
|
||||
* --ignore=path prevents collection of anything below that path
|
||||
* --confcutdir=path only lookup conftest.py test configs below that path
|
||||
* a 'pytest_report_header' hook to add info to the terminal report header
|
||||
* a 'pytestconfig' function argument gives direct access to option values
|
||||
* 'pytest_generate_tests' can now be put into a class as well
|
||||
* on CPython py.test additionally installs as "py.test-VERSION", on
|
||||
Jython as py.test-jython and on PyPy as py.test-pypy-XYZ
|
||||
|
||||
Apart from many bug fixes 1.2.0 also has better pluginization:
|
||||
Distributed testing and looponfailing testing now live in the
|
||||
separately installable 'pytest-xdist' plugin. The same is true for
|
||||
'pytest-figleaf' for doing coverage reporting. Those two plugins
|
||||
can serve well now as blue prints for doing your own.
|
||||
|
||||
thanks to all who helped and gave feedback,
|
||||
have fun,
|
||||
|
||||
holger krekel, January 2010
|
||||
|
||||
Changes between 1.2.0 and 1.1.1
|
||||
=====================================
|
||||
|
||||
- moved dist/looponfailing from py.test core into a new
|
||||
separately released pytest-xdist plugin.
|
||||
|
||||
- new junitxml plugin: --junitxml=path will generate a junit style xml file
|
||||
which is processable e.g. by the Hudson CI system.
|
||||
|
||||
- new option: --genscript=path will generate a standalone py.test script
|
||||
which will not need any libraries installed. thanks to Ralf Schmitt.
|
||||
|
||||
- new option: --ignore will prevent specified path from collection.
|
||||
Can be specified multiple times.
|
||||
|
||||
- new option: --confcutdir=dir will make py.test only consider conftest
|
||||
files that are relative to the specified dir.
|
||||
|
||||
- new funcarg: "pytestconfig" is the pytest config object for access
|
||||
to command line args and can now be easily used in a test.
|
||||
|
||||
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
|
||||
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
|
||||
|
||||
- new "pytestconfig" funcarg allows access to test config object
|
||||
|
||||
- new "pytest_report_header" hook can return additional lines
|
||||
to be displayed at the header of a test run.
|
||||
|
||||
- (experimental) allow "py.test path::name1::name2::..." for pointing
|
||||
to a test within a test collection directly. This might eventually
|
||||
evolve as a full substitute to "-k" specifications.
|
||||
|
||||
- streamlined plugin loading: order is now as documented in
|
||||
customize.html: setuptools, ENV, commandline, conftest.
|
||||
also setuptools entry point names are turned to canonical namees ("pytest_*")
|
||||
|
||||
- automatically skip tests that need 'capfd' but have no os.dup
|
||||
|
||||
- allow pytest_generate_tests to be defined in classes as well
|
||||
|
||||
- deprecate usage of 'disabled' attribute in favour of pytestmark
|
||||
- deprecate definition of Directory, Module, Class and Function nodes
|
||||
in conftest.py files. Use pytest collect hooks instead.
|
||||
|
||||
- collection/item node specific runtest/collect hooks are only called exactly
|
||||
on matching conftest.py files, i.e. ones which are exactly below
|
||||
the filesystem path of an item
|
||||
|
||||
- change: the first pytest_collect_directory hook to return something
|
||||
will now prevent further hooks to be called.
|
||||
|
||||
- change: figleaf plugin now requires --figleaf to run. Also
|
||||
change its long command line options to be a bit shorter (see py.test -h).
|
||||
|
||||
- change: pytest doctest plugin is now enabled by default and has a
|
||||
new option --doctest-glob to set a pattern for file matches.
|
||||
|
||||
- change: remove internal py._* helper vars, only keep py._pydir
|
||||
|
||||
- robustify capturing to survive if custom pytest_runtest_setup
|
||||
code failed and prevented the capturing setup code from running.
|
||||
|
||||
- make py.test.* helpers provided by default plugins visible early -
|
||||
works transparently both for pydoc and for interactive sessions
|
||||
which will regularly see e.g. py.test.mark and py.test.importorskip.
|
||||
|
||||
- simplify internal plugin manager machinery
|
||||
- simplify internal collection tree by introducing a RootCollector node
|
||||
|
||||
- fix assert reinterpreation that sees a call containing "keyword=..."
|
||||
|
||||
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
|
||||
hooks on slaves during dist-testing, report module/session teardown
|
||||
hooks correctly.
|
||||
|
||||
- fix issue65: properly handle dist-testing if no
|
||||
execnet/py lib installed remotely.
|
||||
|
||||
- skip some install-tests if no execnet is available
|
||||
|
||||
- fix docs, fix internal bin/ script generation
|
||||
|
||||
66
doc/announce/release-1.2.1.txt
Normal file
66
doc/announce/release-1.2.1.txt
Normal file
@@ -0,0 +1,66 @@
|
||||
py.test/pylib 1.2.1: little fixes and improvements
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
py.test is an advanced automated testing tool working with
|
||||
Python2, Python3 and Jython versions on all major operating
|
||||
systems. It has a simple plugin architecture and can run many
|
||||
existing common Python test suites without modification. It offers
|
||||
some unique features not found in other testing tools.
|
||||
See http://pytest.org for more info.
|
||||
|
||||
py.test 1.2.1 brings bug fixes and some new options and abilities triggered
|
||||
by user feedback:
|
||||
|
||||
* --funcargs [testpath] will show available builtin- and project funcargs.
|
||||
* display a short and concise traceback if funcarg lookup fails.
|
||||
* early-load "conftest.py" files in non-dot first-level sub directories.
|
||||
* --tb=line will print a single line for each failing test (issue67)
|
||||
* py.cleanup has a number of new options, cleanups up setup.py related files
|
||||
* fix issue78: always call python-level teardown functions even if the
|
||||
according setup failed.
|
||||
|
||||
For more detailed information see the changelog below.
|
||||
|
||||
cheers and have fun,
|
||||
|
||||
holger
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.2.0
|
||||
=====================================
|
||||
|
||||
- refined usage and options for "py.cleanup"::
|
||||
|
||||
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
|
||||
py.cleanup -e .swp -e .cache # also remove files with these extensions
|
||||
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
|
||||
py.cleanup -d # also remove empty directories
|
||||
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
|
||||
py.cleanup -n # dry run, only show what would be removed
|
||||
|
||||
- add a new option "py.test --funcargs" which shows available funcargs
|
||||
and their help strings (docstrings on their respective factory function)
|
||||
for a given test path
|
||||
|
||||
- display a short and concise traceback if a funcarg lookup fails
|
||||
|
||||
- early-load "conftest.py" files in non-dot first-level sub directories.
|
||||
allows to conveniently keep and access test-related options in a ``test``
|
||||
subdir and still add command line options.
|
||||
|
||||
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
|
||||
|
||||
- fix issue78: always call python-level teardown functions even if the
|
||||
according setup failed. This includes refinements for calling setup_module/class functions
|
||||
which will now only be called once instead of the previous behaviour where they'd be called
|
||||
multiple times if they raise an exception (including a Skipped exception). Any exception
|
||||
will be re-corded and associated with all tests in the according module/class scope.
|
||||
|
||||
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
|
||||
|
||||
- fix pdb debugging to be in the correct frame on raises-related errors
|
||||
|
||||
- update apipkg.py to fix an issue where recursive imports might
|
||||
unnecessarily break importing
|
||||
|
||||
- fix plugin links
|
||||
580
doc/announce/release-1.3.0.txt
Normal file
580
doc/announce/release-1.3.0.txt
Normal file
@@ -0,0 +1,580 @@
|
||||
py.test/pylib 1.3.0: new options, per-plugin hooks, fixes ...
|
||||
===========================================================================
|
||||
|
||||
The 1.3.0 release introduces new options, bug fixes and improved compatibility
|
||||
with Python3 and Jython-2.5.1 on Windows. If you already use py-1.2 chances
|
||||
are you can use py-1.3.0. See the below CHANGELOG for more details and
|
||||
http://pylib.org/install.html for installation instructions.
|
||||
|
||||
py.test is an advanced automated testing tool working with Python2,
|
||||
Python3, Jython and PyPy versions on all major operating systems. It
|
||||
offers a no-boilerplate testing approach and has inspired other testing
|
||||
tools and enhancements in the standard Python library for more than five
|
||||
years. It has a simple and extensive plugin architecture, configurable
|
||||
reporting and provides unique ways to make it fit to your testing
|
||||
process and needs.
|
||||
|
||||
See http://pytest.org for more info.
|
||||
|
||||
cheers and have fun,
|
||||
|
||||
holger krekel
|
||||
|
||||
Changes between 1.2.1 and 1.3.0
|
||||
==================================================
|
||||
|
||||
- deprecate --report option in favour of a new shorter and easier to
|
||||
remember -r option: it takes a string argument consisting of any
|
||||
combination of 'xfsX' characters. They relate to the single chars
|
||||
you see during the dotted progress printing and will print an extra line
|
||||
per test at the end of the test run. This extra line indicates the exact
|
||||
position or test ID that you directly paste to the py.test cmdline in order
|
||||
to re-run a particular test.
|
||||
|
||||
- allow external plugins to register new hooks via the new
|
||||
pytest_addhooks(pluginmanager) hook. The new release of
|
||||
the pytest-xdist plugin for distributed and looponfailing
|
||||
testing requires this feature.
|
||||
|
||||
- add a new pytest_ignore_collect(path, config) hook to allow projects and
|
||||
plugins to define exclusion behaviour for their directory structure -
|
||||
for example you may define in a conftest.py this method::
|
||||
|
||||
def pytest_ignore_collect(path):
|
||||
return path.check(link=1)
|
||||
|
||||
to prevent even collection of any tests in symlinked dirs.
|
||||
|
||||
- new pytest_pycollect_makemodule(path, parent) hook for
|
||||
allowing customization of the Module collection object for a
|
||||
matching test module.
|
||||
|
||||
- extend and refine xfail mechanism::
|
||||
|
||||
@py.test.mark.xfail(run=False) do not run the decorated test
|
||||
@py.test.mark.xfail(reason="...") prints the reason string in xfail summaries
|
||||
|
||||
specifiying ``--runxfail`` on command line ignores xfail markers to show
|
||||
you the underlying traceback.
|
||||
|
||||
- expose (previously internal) commonly useful methods:
|
||||
py.io.get_terminal_with() -> return terminal width
|
||||
py.io.ansi_print(...) -> print colored/bold text on linux/win32
|
||||
py.io.saferepr(obj) -> return limited representation string
|
||||
|
||||
- expose test outcome related exceptions as py.test.skip.Exception,
|
||||
py.test.raises.Exception etc., useful mostly for plugins
|
||||
doing special outcome interpretation/tweaking
|
||||
|
||||
- (issue85) fix junitxml plugin to handle tests with non-ascii output
|
||||
|
||||
- fix/refine python3 compatibility (thanks Benjamin Peterson)
|
||||
|
||||
- fixes for making the jython/win32 combination work, note however:
|
||||
jython2.5.1/win32 does not provide a command line launcher, see
|
||||
http://bugs.jython.org/issue1491 . See pylib install documentation
|
||||
for how to work around.
|
||||
|
||||
- fixes for handling of unicode exception values and unprintable objects
|
||||
|
||||
- (issue87) fix unboundlocal error in assertionold code
|
||||
|
||||
- (issue86) improve documentation for looponfailing
|
||||
|
||||
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
|
||||
|
||||
- ship distribute_setup.py version 0.6.10
|
||||
|
||||
- added links to the new capturelog and coverage plugins
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.2.0
|
||||
=====================================
|
||||
|
||||
- refined usage and options for "py.cleanup"::
|
||||
|
||||
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
|
||||
py.cleanup -e .swp -e .cache # also remove files with these extensions
|
||||
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
|
||||
py.cleanup -d # also remove empty directories
|
||||
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
|
||||
py.cleanup -n # dry run, only show what would be removed
|
||||
|
||||
- add a new option "py.test --funcargs" which shows available funcargs
|
||||
and their help strings (docstrings on their respective factory function)
|
||||
for a given test path
|
||||
|
||||
- display a short and concise traceback if a funcarg lookup fails
|
||||
|
||||
- early-load "conftest.py" files in non-dot first-level sub directories.
|
||||
allows to conveniently keep and access test-related options in a ``test``
|
||||
subdir and still add command line options.
|
||||
|
||||
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
|
||||
|
||||
- fix issue78: always call python-level teardown functions even if the
|
||||
according setup failed. This includes refinements for calling setup_module/class functions
|
||||
which will now only be called once instead of the previous behaviour where they'd be called
|
||||
multiple times if they raise an exception (including a Skipped exception). Any exception
|
||||
will be re-corded and associated with all tests in the according module/class scope.
|
||||
|
||||
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
|
||||
|
||||
- fix pdb debugging to be in the correct frame on raises-related errors
|
||||
|
||||
- update apipkg.py to fix an issue where recursive imports might
|
||||
unnecessarily break importing
|
||||
|
||||
- fix plugin links
|
||||
|
||||
Changes between 1.2 and 1.1.1
|
||||
=====================================
|
||||
|
||||
- moved dist/looponfailing from py.test core into a new
|
||||
separately released pytest-xdist plugin.
|
||||
|
||||
- new junitxml plugin: --junitxml=path will generate a junit style xml file
|
||||
which is processable e.g. by the Hudson CI system.
|
||||
|
||||
- new option: --genscript=path will generate a standalone py.test script
|
||||
which will not need any libraries installed. thanks to Ralf Schmitt.
|
||||
|
||||
- new option: --ignore will prevent specified path from collection.
|
||||
Can be specified multiple times.
|
||||
|
||||
- new option: --confcutdir=dir will make py.test only consider conftest
|
||||
files that are relative to the specified dir.
|
||||
|
||||
- new funcarg: "pytestconfig" is the pytest config object for access
|
||||
to command line args and can now be easily used in a test.
|
||||
|
||||
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
|
||||
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
|
||||
|
||||
- new "pytestconfig" funcarg allows access to test config object
|
||||
|
||||
- new "pytest_report_header" hook can return additional lines
|
||||
to be displayed at the header of a test run.
|
||||
|
||||
- (experimental) allow "py.test path::name1::name2::..." for pointing
|
||||
to a test within a test collection directly. This might eventually
|
||||
evolve as a full substitute to "-k" specifications.
|
||||
|
||||
- streamlined plugin loading: order is now as documented in
|
||||
customize.html: setuptools, ENV, commandline, conftest.
|
||||
also setuptools entry point names are turned to canonical namees ("pytest_*")
|
||||
|
||||
- automatically skip tests that need 'capfd' but have no os.dup
|
||||
|
||||
- allow pytest_generate_tests to be defined in classes as well
|
||||
|
||||
- deprecate usage of 'disabled' attribute in favour of pytestmark
|
||||
- deprecate definition of Directory, Module, Class and Function nodes
|
||||
in conftest.py files. Use pytest collect hooks instead.
|
||||
|
||||
- collection/item node specific runtest/collect hooks are only called exactly
|
||||
on matching conftest.py files, i.e. ones which are exactly below
|
||||
the filesystem path of an item
|
||||
|
||||
- change: the first pytest_collect_directory hook to return something
|
||||
will now prevent further hooks to be called.
|
||||
|
||||
- change: figleaf plugin now requires --figleaf to run. Also
|
||||
change its long command line options to be a bit shorter (see py.test -h).
|
||||
|
||||
- change: pytest doctest plugin is now enabled by default and has a
|
||||
new option --doctest-glob to set a pattern for file matches.
|
||||
|
||||
- change: remove internal py._* helper vars, only keep py._pydir
|
||||
|
||||
- robustify capturing to survive if custom pytest_runtest_setup
|
||||
code failed and prevented the capturing setup code from running.
|
||||
|
||||
- make py.test.* helpers provided by default plugins visible early -
|
||||
works transparently both for pydoc and for interactive sessions
|
||||
which will regularly see e.g. py.test.mark and py.test.importorskip.
|
||||
|
||||
- simplify internal plugin manager machinery
|
||||
- simplify internal collection tree by introducing a RootCollector node
|
||||
|
||||
- fix assert reinterpreation that sees a call containing "keyword=..."
|
||||
|
||||
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
|
||||
hooks on slaves during dist-testing, report module/session teardown
|
||||
hooks correctly.
|
||||
|
||||
- fix issue65: properly handle dist-testing if no
|
||||
execnet/py lib installed remotely.
|
||||
|
||||
- skip some install-tests if no execnet is available
|
||||
|
||||
- fix docs, fix internal bin/ script generation
|
||||
|
||||
|
||||
Changes between 1.1.1 and 1.1.0
|
||||
=====================================
|
||||
|
||||
- introduce automatic plugin registration via 'pytest11'
|
||||
entrypoints via setuptools' pkg_resources.iter_entry_points
|
||||
|
||||
- fix py.test dist-testing to work with execnet >= 1.0.0b4
|
||||
|
||||
- re-introduce py.test.cmdline.main() for better backward compatibility
|
||||
|
||||
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
|
||||
allow '%' in svn paths, make svnwc.update() default to interactive mode
|
||||
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
|
||||
|
||||
- refine distributed tarball to contain test and no pyc files
|
||||
|
||||
- try harder to have deprecation warnings for py.compat.* accesses
|
||||
report a correct location
|
||||
|
||||
Changes between 1.1.0 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* adjust and improve docs
|
||||
|
||||
* remove py.rest tool and internal namespace - it was
|
||||
never really advertised and can still be used with
|
||||
the old release if needed. If there is interest
|
||||
it could be revived into its own tool i guess.
|
||||
|
||||
* fix issue48 and issue59: raise an Error if the module
|
||||
from an imported test file does not seem to come from
|
||||
the filepath - avoids "same-name" confusion that has
|
||||
been reported repeatedly
|
||||
|
||||
* merged Ronny's nose-compatibility hacks: now
|
||||
nose-style setup_module() and setup() functions are
|
||||
supported
|
||||
|
||||
* introduce generalized py.test.mark function marking
|
||||
|
||||
* reshuffle / refine command line grouping
|
||||
|
||||
* deprecate parser.addgroup in favour of getgroup which creates option group
|
||||
|
||||
* add --report command line option that allows to control showing of skipped/xfailed sections
|
||||
|
||||
* generalized skipping: a new way to mark python functions with skipif or xfail
|
||||
at function, class and modules level based on platform or sys-module attributes.
|
||||
|
||||
* extend py.test.mark decorator to allow for positional args
|
||||
|
||||
* introduce and test "py.cleanup -d" to remove empty directories
|
||||
|
||||
* fix issue #59 - robustify unittest test collection
|
||||
|
||||
* make bpython/help interaction work by adding an __all__ attribute
|
||||
to ApiModule, cleanup initpkg
|
||||
|
||||
* use MIT license for pylib, add some contributors
|
||||
|
||||
* remove py.execnet code and substitute all usages with 'execnet' proper
|
||||
|
||||
* fix issue50 - cached_setup now caches more to expectations
|
||||
for test functions with multiple arguments.
|
||||
|
||||
* merge Jarko's fixes, issue #45 and #46
|
||||
|
||||
* add the ability to specify a path for py.lookup to search in
|
||||
|
||||
* fix a funcarg cached_setup bug probably only occuring
|
||||
in distributed testing and "module" scope with teardown.
|
||||
|
||||
* many fixes and changes for making the code base python3 compatible,
|
||||
many thanks to Benjamin Peterson for helping with this.
|
||||
|
||||
* consolidate builtins implementation to be compatible with >=2.3,
|
||||
add helpers to ease keeping 2 and 3k compatible code
|
||||
|
||||
* deprecate py.compat.doctest|subprocess|textwrap|optparse
|
||||
|
||||
* deprecate py.magic.autopath, remove py/magic directory
|
||||
|
||||
* move pytest assertion handling to py/code and a pytest_assertion
|
||||
plugin, add "--no-assert" option, deprecate py.magic namespaces
|
||||
in favour of (less) py.code ones.
|
||||
|
||||
* consolidate and cleanup py/code classes and files
|
||||
|
||||
* cleanup py/misc, move tests to bin-for-dist
|
||||
|
||||
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
|
||||
|
||||
* consolidate py.log implementation, remove old approach.
|
||||
|
||||
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
||||
text/unicode and byte-streams (uses underlying standard lib io.*
|
||||
if available)
|
||||
|
||||
* make py.unittest_convert helper script available which converts "unittest.py"
|
||||
style files into the simpler assert/direct-test-classes py.test/nosetests
|
||||
style. The script was written by Laura Creighton.
|
||||
|
||||
* simplified internal localpath implementation
|
||||
|
||||
Changes between 1.0.1 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* fixing packaging issues, triggered by fedora redhat packaging,
|
||||
also added doc, examples and contrib dirs to the tarball.
|
||||
|
||||
* added a documentation link to the new django plugin.
|
||||
|
||||
Changes between 1.0.0 and 1.0.1
|
||||
=====================================
|
||||
|
||||
* added a 'pytest_nose' plugin which handles nose.SkipTest,
|
||||
nose-style function/method/generator setup/teardown and
|
||||
tries to report functions correctly.
|
||||
|
||||
* capturing of unicode writes or encoded strings to sys.stdout/err
|
||||
work better, also terminalwriting was adapted and somewhat
|
||||
unified between windows and linux.
|
||||
|
||||
* improved documentation layout and content a lot
|
||||
|
||||
* added a "--help-config" option to show conftest.py / ENV-var names for
|
||||
all longopt cmdline options, and some special conftest.py variables.
|
||||
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
|
||||
|
||||
* fix issue #27: better reporting on non-collectable items given on commandline
|
||||
(e.g. pyc files)
|
||||
|
||||
* fix issue #33: added --version flag (thanks Benjamin Peterson)
|
||||
|
||||
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
|
||||
|
||||
* "Test" prefixed classes are *not* collected by default anymore if they
|
||||
have an __init__ method
|
||||
|
||||
* monkeypatch setenv() now accepts a "prepend" parameter
|
||||
|
||||
* improved reporting of collection error tracebacks
|
||||
|
||||
* simplified multicall mechanism and plugin architecture,
|
||||
renamed some internal methods and argnames
|
||||
|
||||
Changes between 1.0.0b9 and 1.0.0
|
||||
=====================================
|
||||
|
||||
* more terse reporting try to show filesystem path relatively to current dir
|
||||
* improve xfail output a bit
|
||||
|
||||
Changes between 1.0.0b8 and 1.0.0b9
|
||||
=====================================
|
||||
|
||||
* cleanly handle and report final teardown of test setup
|
||||
|
||||
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
|
||||
(thanks Wouter Vanden Hove)
|
||||
|
||||
* setup/teardown or collection problems now show as ERRORs
|
||||
or with big "E"'s in the progress lines. they are reported
|
||||
and counted separately.
|
||||
|
||||
* dist-testing: properly handle test items that get locally
|
||||
collected but cannot be collected on the remote side - often
|
||||
due to platform/dependency reasons
|
||||
|
||||
* simplified py.test.mark API - see keyword plugin documentation
|
||||
|
||||
* integrate better with logging: capturing now by default captures
|
||||
test functions and their immediate setup/teardown in a single stream
|
||||
|
||||
* capsys and capfd funcargs now have a readouterr() and a close() method
|
||||
(underlyingly py.io.StdCapture/FD objects are used which grew a
|
||||
readouterr() method as well to return snapshots of captured out/err)
|
||||
|
||||
* make assert-reinterpretation work better with comparisons not
|
||||
returning bools (reported with numpy from thanks maciej fijalkowski)
|
||||
|
||||
* reworked per-test output capturing into the pytest_iocapture.py plugin
|
||||
and thus removed capturing code from config object
|
||||
|
||||
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
|
||||
|
||||
|
||||
Changes between 1.0.0b7 and 1.0.0b8
|
||||
=====================================
|
||||
|
||||
* pytest_unittest-plugin is now enabled by default
|
||||
|
||||
* introduced pytest_keyboardinterrupt hook and
|
||||
refined pytest_sessionfinish hooked, added tests.
|
||||
|
||||
* workaround a buggy logging module interaction ("closing already closed
|
||||
files"). Thanks to Sridhar Ratnakumar for triggering.
|
||||
|
||||
* if plugins use "py.test.importorskip" for importing
|
||||
a dependency only a warning will be issued instead
|
||||
of exiting the testing process.
|
||||
|
||||
* many improvements to docs:
|
||||
- refined funcargs doc , use the term "factory" instead of "provider"
|
||||
- added a new talk/tutorial doc page
|
||||
- better download page
|
||||
- better plugin docstrings
|
||||
- added new plugins page and automatic doc generation script
|
||||
|
||||
* fixed teardown problem related to partially failing funcarg setups
|
||||
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
|
||||
always invoked even if the "pytest_runtest_setup" failed.
|
||||
|
||||
* tweaked doctest output for docstrings in py modules,
|
||||
thanks Radomir.
|
||||
|
||||
Changes between 1.0.0b3 and 1.0.0b7
|
||||
=============================================
|
||||
|
||||
* renamed py.test.xfail back to py.test.mark.xfail to avoid
|
||||
two ways to decorate for xfail
|
||||
|
||||
* re-added py.test.mark decorator for setting keywords on functions
|
||||
(it was actually documented so removing it was not nice)
|
||||
|
||||
* remove scope-argument from request.addfinalizer() because
|
||||
request.cached_setup has the scope arg. TOOWTDI.
|
||||
|
||||
* perform setup finalization before reporting failures
|
||||
|
||||
* apply modified patches from Andreas Kloeckner to allow
|
||||
test functions to have no func_code (#22) and to make
|
||||
"-k" and function keywords work (#20)
|
||||
|
||||
* apply patch from Daniel Peolzleithner (issue #23)
|
||||
|
||||
* resolve issue #18, multiprocessing.Manager() and
|
||||
redirection clash
|
||||
|
||||
* make __name__ == "__channelexec__" for remote_exec code
|
||||
|
||||
Changes between 1.0.0b1 and 1.0.0b3
|
||||
=============================================
|
||||
|
||||
* plugin classes are removed: one now defines
|
||||
hooks directly in conftest.py or global pytest_*.py
|
||||
files.
|
||||
|
||||
* added new pytest_namespace(config) hook that allows
|
||||
to inject helpers directly to the py.test.* namespace.
|
||||
|
||||
* documented and refined many hooks
|
||||
|
||||
* added new style of generative tests via
|
||||
pytest_generate_tests hook that integrates
|
||||
well with function arguments.
|
||||
|
||||
|
||||
Changes between 0.9.2 and 1.0.0b1
|
||||
=============================================
|
||||
|
||||
* introduced new "funcarg" setup method,
|
||||
see doc/test/funcarg.txt
|
||||
|
||||
* introduced plugin architecuture and many
|
||||
new py.test plugins, see
|
||||
doc/test/plugins.txt
|
||||
|
||||
* teardown_method is now guaranteed to get
|
||||
called after a test method has run.
|
||||
|
||||
* new method: py.test.importorskip(mod,minversion)
|
||||
will either import or call py.test.skip()
|
||||
|
||||
* completely revised internal py.test architecture
|
||||
|
||||
* new py.process.ForkedFunc object allowing to
|
||||
fork execution of a function to a sub process
|
||||
and getting a result back.
|
||||
|
||||
XXX lots of things missing here XXX
|
||||
|
||||
Changes between 0.9.1 and 0.9.2
|
||||
===============================
|
||||
|
||||
* refined installation and metadata, created new setup.py,
|
||||
now based on setuptools/ez_setup (thanks to Ralf Schmitt
|
||||
for his support).
|
||||
|
||||
* improved the way of making py.* scripts available in
|
||||
windows environments, they are now added to the
|
||||
Scripts directory as ".cmd" files.
|
||||
|
||||
* py.path.svnwc.status() now is more complete and
|
||||
uses xml output from the 'svn' command if available
|
||||
(Guido Wesdorp)
|
||||
|
||||
* fix for py.path.svn* to work with svn 1.5
|
||||
(Chris Lamb)
|
||||
|
||||
* fix path.relto(otherpath) method on windows to
|
||||
use normcase for checking if a path is relative.
|
||||
|
||||
* py.test's traceback is better parseable from editors
|
||||
(follows the filenames:LINENO: MSG convention)
|
||||
(thanks to Osmo Salomaa)
|
||||
|
||||
* fix to javascript-generation, "py.test --runbrowser"
|
||||
should work more reliably now
|
||||
|
||||
* removed previously accidentally added
|
||||
py.test.broken and py.test.notimplemented helpers.
|
||||
|
||||
* there now is a py.__version__ attribute
|
||||
|
||||
Changes between 0.9.0 and 0.9.1
|
||||
===============================
|
||||
|
||||
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
|
||||
serve as a reference for developers.
|
||||
|
||||
* allowing + signs in py.path.svn urls [39106]
|
||||
* fixed support for Failed exceptions without excinfo in py.test [39340]
|
||||
* added support for killing processes for Windows (as well as platforms that
|
||||
support os.kill) in py.misc.killproc [39655]
|
||||
* added setup/teardown for generative tests to py.test [40702]
|
||||
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
|
||||
* fixed problem with calling .remove() on wcpaths of non-versioned files in
|
||||
py.path [44248]
|
||||
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
|
||||
* fail to run greenlet tests when pypy is available, but without stackless
|
||||
[45294]
|
||||
* small fixes in rsession tests [45295]
|
||||
* fixed issue with 2.5 type representations in py.test [45483, 45484]
|
||||
* made that internal reporting issues displaying is done atomically in py.test
|
||||
[45518]
|
||||
* made that non-existing files are igored by the py.lookup script [45519]
|
||||
* improved exception name creation in py.test [45535]
|
||||
* made that less threads are used in execnet [merge in 45539]
|
||||
* removed lock required for atomical reporting issue displaying in py.test
|
||||
[45545]
|
||||
* removed globals from execnet [45541, 45547]
|
||||
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
|
||||
get called in 2.5 (py.execnet) [45548]
|
||||
* fixed bug in joining threads in py.execnet's servemain [45549]
|
||||
* refactored py.test.rsession tests to not rely on exact output format anymore
|
||||
[45646]
|
||||
* using repr() on test outcome [45647]
|
||||
* added 'Reason' classes for py.test.skip() [45648, 45649]
|
||||
* killed some unnecessary sanity check in py.test.collect [45655]
|
||||
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
|
||||
usable by Administrators [45901]
|
||||
* added support for locking and non-recursive commits to py.path.svnwc [45994]
|
||||
* locking files in py.execnet to prevent CPython from segfaulting [46010]
|
||||
* added export() method to py.path.svnurl
|
||||
* fixed -d -x in py.test [47277]
|
||||
* fixed argument concatenation problem in py.path.svnwc [49423]
|
||||
* restore py.test behaviour that it exits with code 1 when there are failures
|
||||
[49974]
|
||||
* don't fail on html files that don't have an accompanying .txt file [50606]
|
||||
* fixed 'utestconvert.py < input' [50645]
|
||||
* small fix for code indentation in py.code.source [50755]
|
||||
* fix _docgen.py documentation building [51285]
|
||||
* improved checks for source representation of code blocks in py.test [51292]
|
||||
* added support for passing authentication to py.path.svn* objects [52000,
|
||||
52001]
|
||||
* removed sorted() call for py.apigen tests in favour of [].sort() to support
|
||||
Python 2.3 [52481]
|
||||
104
doc/announce/release-1.3.1.txt
Normal file
104
doc/announce/release-1.3.1.txt
Normal file
@@ -0,0 +1,104 @@
|
||||
py.test/pylib 1.3.1: new py.test.xfail, --maxfail, better reporting
|
||||
===========================================================================
|
||||
|
||||
The pylib/py.test 1.3.1 release brings:
|
||||
|
||||
- the new imperative ``py.test.xfail()`` helper in order to have a test or
|
||||
setup function result in an "expected failure"
|
||||
- a new option ``--maxfail=NUM`` to stop the test run after some failures
|
||||
- markers/decorators are now applicable to test classes (>=Python2.6)
|
||||
- improved reporting, shorter tracebacks in several cases
|
||||
- some simplified internals, more compatibility with Jython and PyPy
|
||||
- bug fixes and various refinements
|
||||
|
||||
See the below CHANGELOG entry below for more details and
|
||||
http://pylib.org/install.html for installation instructions.
|
||||
|
||||
If you used older versions of py.test you should be able to upgrade
|
||||
to 1.3.1 without changes to your test source code.
|
||||
|
||||
py.test is an automated testing tool working with Python2,
|
||||
Python3, Jython and PyPy versions on all major operating systems. It
|
||||
offers a no-boilerplate testing approach and has inspired other testing
|
||||
tools and enhancements in the standard Python library for more than five
|
||||
years. It has a simple and extensive plugin architecture, configurable
|
||||
reporting and provides unique ways to make it fit to your testing
|
||||
process and needs.
|
||||
|
||||
See http://pytest.org for more info.
|
||||
|
||||
cheers and have fun,
|
||||
|
||||
holger krekel
|
||||
|
||||
Changes between 1.3.0 and 1.3.1
|
||||
==================================================
|
||||
|
||||
New features
|
||||
++++++++++++++++++
|
||||
|
||||
- issue91: introduce new py.test.xfail(reason) helper
|
||||
to imperatively mark a test as expected to fail. Can
|
||||
be used from within setup and test functions. This is
|
||||
useful especially for parametrized tests when certain
|
||||
configurations are expected-to-fail. In this case the
|
||||
declarative approach with the @py.test.mark.xfail cannot
|
||||
be used as it would mark all configurations as xfail.
|
||||
|
||||
- issue102: introduce new --maxfail=NUM option to stop
|
||||
test runs after NUM failures. This is a generalization
|
||||
of the '-x' or '--exitfirst' option which is now equivalent
|
||||
to '--maxfail=1'. Both '-x' and '--maxfail' will
|
||||
now also print a line near the end indicating the Interruption.
|
||||
|
||||
- issue89: allow py.test.mark decorators to be used on classes
|
||||
(class decorators were introduced with python2.6) and
|
||||
also allow to have multiple markers applied at class/module level
|
||||
by specifying a list.
|
||||
|
||||
- improve and refine letter reporting in the progress bar:
|
||||
. pass
|
||||
f failed test
|
||||
s skipped tests (reminder: use for dependency/platform mismatch only)
|
||||
x xfailed test (test that was expected to fail)
|
||||
X xpassed test (test that was expected to fail but passed)
|
||||
|
||||
You can use any combination of 'fsxX' with the '-r' extended
|
||||
reporting option. The xfail/xpass results will show up as
|
||||
skipped tests in the junitxml output - which also fixes
|
||||
issue99.
|
||||
|
||||
- make py.test.cmdline.main() return the exitstatus instead of raising
|
||||
SystemExit and also allow it to be called multiple times. This of
|
||||
course requires that your application and tests are properly teared
|
||||
down and don't have global state.
|
||||
|
||||
Fixes / Maintenance
|
||||
++++++++++++++++++++++
|
||||
|
||||
- improved traceback presentation:
|
||||
- improved and unified reporting for "--tb=short" option
|
||||
- Errors during test module imports are much shorter, (using --tb=short style)
|
||||
- raises shows shorter more relevant tracebacks
|
||||
- --fulltrace now more systematically makes traces longer / inhibits cutting
|
||||
|
||||
- improve support for raises and other dynamically compiled code by
|
||||
manipulating python's linecache.cache instead of the previous
|
||||
rather hacky way of creating custom code objects. This makes
|
||||
it seemlessly work on Jython and PyPy where it previously didn't.
|
||||
|
||||
- fix issue96: make capturing more resilient against Control-C
|
||||
interruptions (involved somewhat substantial refactoring
|
||||
to the underlying capturing functionality to avoid race
|
||||
conditions).
|
||||
|
||||
- fix chaining of conditional skipif/xfail decorators - so it works now
|
||||
as expected to use multiple @py.test.mark.skipif(condition) decorators,
|
||||
including specific reporting which of the conditions lead to skipping.
|
||||
|
||||
- fix issue95: late-import zlib so that it's not required
|
||||
for general py.test startup.
|
||||
|
||||
- fix issue94: make reporting more robust against bogus source code
|
||||
(and internally be more careful when presenting unexpected byte sequences)
|
||||
|
||||
720
doc/announce/release-1.3.2.txt
Normal file
720
doc/announce/release-1.3.2.txt
Normal file
@@ -0,0 +1,720 @@
|
||||
py.test/pylib 1.3.2: API and reporting refinements, many fixes
|
||||
===========================================================================
|
||||
|
||||
The pylib/py.test 1.3.2 release brings many bug fixes and some new
|
||||
features. It was refined for and tested against the recently released
|
||||
Python2.7 and remains compatibile to the usual armada of interpreters
|
||||
(Python2.4 through to Python3.1.2, Jython and PyPy). Note that for using
|
||||
distributed testing features you'll need to upgrade to the jointly released
|
||||
pytest-xdist-1.4 because of some internal refactorings.
|
||||
|
||||
See http://pytest.org for general documentation and below for
|
||||
a detailed CHANGELOG.
|
||||
|
||||
cheers & particular thanks to Benjamin Peterson, Ronny Pfannschmidt
|
||||
and all issue and patch contributors,
|
||||
|
||||
holger krekel
|
||||
|
||||
Changes between 1.3.1 and 1.3.2
|
||||
==================================================
|
||||
|
||||
New features
|
||||
++++++++++++++++++
|
||||
|
||||
- fix issue103: introduce py.test.raises as context manager, examples::
|
||||
|
||||
with py.test.raises(ZeroDivisionError):
|
||||
x = 0
|
||||
1 / x
|
||||
|
||||
with py.test.raises(RuntimeError) as excinfo:
|
||||
call_something()
|
||||
|
||||
# you may do extra checks on excinfo.value|type|traceback here
|
||||
|
||||
(thanks Ronny Pfannschmidt)
|
||||
|
||||
- Funcarg factories can now dynamically apply a marker to a
|
||||
test invocation. This is for example useful if a factory
|
||||
provides parameters to a test which are expected-to-fail::
|
||||
|
||||
def pytest_funcarg__arg(request):
|
||||
request.applymarker(py.test.mark.xfail(reason="flaky config"))
|
||||
...
|
||||
|
||||
def test_function(arg):
|
||||
...
|
||||
|
||||
- improved error reporting on collection and import errors. This makes
|
||||
use of a more general mechanism, namely that for custom test item/collect
|
||||
nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
|
||||
override it to return a string error representation of your choice
|
||||
which is going to be reported as a (red) string.
|
||||
|
||||
- introduce '--junitprefix=STR' option to prepend a prefix
|
||||
to all reports in the junitxml file.
|
||||
|
||||
Bug fixes / Maintenance
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
|
||||
to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
|
||||
you can properly check for their existence in a cross-python manner).
|
||||
- refine --pdb: ignore xfailed tests, unify its TB-reporting and
|
||||
don't display failures again at the end.
|
||||
- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
|
||||
- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
|
||||
- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
|
||||
- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
|
||||
- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
|
||||
- fix py.code.compile(source) to generate unique filenames
|
||||
- fix assertion re-interp problems on PyPy, by defering code
|
||||
compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
|
||||
- fix py.path.local.pyimport() to work with directories
|
||||
- streamline py.path.local.mkdtemp implementation and usage
|
||||
- don't print empty lines when showing junitxml-filename
|
||||
- add optional boolean ignore_errors parameter to py.path.local.remove
|
||||
- fix terminal writing on win32/python2.4
|
||||
- py.process.cmdexec() now tries harder to return properly encoded unicode objects
|
||||
on all python versions
|
||||
- install plain py.test/py.which scripts also for Jython, this helps to
|
||||
get canonical script paths in virtualenv situations
|
||||
- make path.bestrelpath(path) return ".", note that when calling
|
||||
X.bestrelpath the assumption is that X is a directory.
|
||||
- make initial conftest discovery ignore "--" prefixed arguments
|
||||
- fix resultlog plugin when used in an multicpu/multihost xdist situation
|
||||
(thanks Jakub Gustak)
|
||||
- perform distributed testing related reporting in the xdist-plugin
|
||||
rather than having dist-related code in the generic py.test
|
||||
distribution
|
||||
- fix homedir detection on Windows
|
||||
- ship distribute_setup.py version 0.6.13
|
||||
|
||||
Changes between 1.3.0 and 1.3.1
|
||||
==================================================
|
||||
|
||||
New features
|
||||
++++++++++++++++++
|
||||
|
||||
- issue91: introduce new py.test.xfail(reason) helper
|
||||
to imperatively mark a test as expected to fail. Can
|
||||
be used from within setup and test functions. This is
|
||||
useful especially for parametrized tests when certain
|
||||
configurations are expected-to-fail. In this case the
|
||||
declarative approach with the @py.test.mark.xfail cannot
|
||||
be used as it would mark all configurations as xfail.
|
||||
|
||||
- issue102: introduce new --maxfail=NUM option to stop
|
||||
test runs after NUM failures. This is a generalization
|
||||
of the '-x' or '--exitfirst' option which is now equivalent
|
||||
to '--maxfail=1'. Both '-x' and '--maxfail' will
|
||||
now also print a line near the end indicating the Interruption.
|
||||
|
||||
- issue89: allow py.test.mark decorators to be used on classes
|
||||
(class decorators were introduced with python2.6) and
|
||||
also allow to have multiple markers applied at class/module level
|
||||
by specifying a list.
|
||||
|
||||
- improve and refine letter reporting in the progress bar:
|
||||
. pass
|
||||
f failed test
|
||||
s skipped tests (reminder: use for dependency/platform mismatch only)
|
||||
x xfailed test (test that was expected to fail)
|
||||
X xpassed test (test that was expected to fail but passed)
|
||||
|
||||
You can use any combination of 'fsxX' with the '-r' extended
|
||||
reporting option. The xfail/xpass results will show up as
|
||||
skipped tests in the junitxml output - which also fixes
|
||||
issue99.
|
||||
|
||||
- make py.test.cmdline.main() return the exitstatus instead of raising
|
||||
SystemExit and also allow it to be called multiple times. This of
|
||||
course requires that your application and tests are properly teared
|
||||
down and don't have global state.
|
||||
|
||||
Fixes / Maintenance
|
||||
++++++++++++++++++++++
|
||||
|
||||
- improved traceback presentation:
|
||||
- improved and unified reporting for "--tb=short" option
|
||||
- Errors during test module imports are much shorter, (using --tb=short style)
|
||||
- raises shows shorter more relevant tracebacks
|
||||
- --fulltrace now more systematically makes traces longer / inhibits cutting
|
||||
|
||||
- improve support for raises and other dynamically compiled code by
|
||||
manipulating python's linecache.cache instead of the previous
|
||||
rather hacky way of creating custom code objects. This makes
|
||||
it seemlessly work on Jython and PyPy where it previously didn't.
|
||||
|
||||
- fix issue96: make capturing more resilient against Control-C
|
||||
interruptions (involved somewhat substantial refactoring
|
||||
to the underlying capturing functionality to avoid race
|
||||
conditions).
|
||||
|
||||
- fix chaining of conditional skipif/xfail decorators - so it works now
|
||||
as expected to use multiple @py.test.mark.skipif(condition) decorators,
|
||||
including specific reporting which of the conditions lead to skipping.
|
||||
|
||||
- fix issue95: late-import zlib so that it's not required
|
||||
for general py.test startup.
|
||||
|
||||
- fix issue94: make reporting more robust against bogus source code
|
||||
(and internally be more careful when presenting unexpected byte sequences)
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.3.0
|
||||
==================================================
|
||||
|
||||
- deprecate --report option in favour of a new shorter and easier to
|
||||
remember -r option: it takes a string argument consisting of any
|
||||
combination of 'xfsX' characters. They relate to the single chars
|
||||
you see during the dotted progress printing and will print an extra line
|
||||
per test at the end of the test run. This extra line indicates the exact
|
||||
position or test ID that you directly paste to the py.test cmdline in order
|
||||
to re-run a particular test.
|
||||
|
||||
- allow external plugins to register new hooks via the new
|
||||
pytest_addhooks(pluginmanager) hook. The new release of
|
||||
the pytest-xdist plugin for distributed and looponfailing
|
||||
testing requires this feature.
|
||||
|
||||
- add a new pytest_ignore_collect(path, config) hook to allow projects and
|
||||
plugins to define exclusion behaviour for their directory structure -
|
||||
for example you may define in a conftest.py this method::
|
||||
|
||||
def pytest_ignore_collect(path):
|
||||
return path.check(link=1)
|
||||
|
||||
to prevent even a collection try of any tests in symlinked dirs.
|
||||
|
||||
- new pytest_pycollect_makemodule(path, parent) hook for
|
||||
allowing customization of the Module collection object for a
|
||||
matching test module.
|
||||
|
||||
- extend and refine xfail mechanism:
|
||||
``@py.test.mark.xfail(run=False)`` do not run the decorated test
|
||||
``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
|
||||
specifiying ``--runxfail`` on command line virtually ignores xfail markers
|
||||
|
||||
- expose (previously internal) commonly useful methods:
|
||||
py.io.get_terminal_with() -> return terminal width
|
||||
py.io.ansi_print(...) -> print colored/bold text on linux/win32
|
||||
py.io.saferepr(obj) -> return limited representation string
|
||||
|
||||
- expose test outcome related exceptions as py.test.skip.Exception,
|
||||
py.test.raises.Exception etc., useful mostly for plugins
|
||||
doing special outcome interpretation/tweaking
|
||||
|
||||
- (issue85) fix junitxml plugin to handle tests with non-ascii output
|
||||
|
||||
- fix/refine python3 compatibility (thanks Benjamin Peterson)
|
||||
|
||||
- fixes for making the jython/win32 combination work, note however:
|
||||
jython2.5.1/win32 does not provide a command line launcher, see
|
||||
http://bugs.jython.org/issue1491 . See pylib install documentation
|
||||
for how to work around.
|
||||
|
||||
- fixes for handling of unicode exception values and unprintable objects
|
||||
|
||||
- (issue87) fix unboundlocal error in assertionold code
|
||||
|
||||
- (issue86) improve documentation for looponfailing
|
||||
|
||||
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
|
||||
|
||||
- ship distribute_setup.py version 0.6.10
|
||||
|
||||
- added links to the new capturelog and coverage plugins
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.2.0
|
||||
=====================================
|
||||
|
||||
- refined usage and options for "py.cleanup"::
|
||||
|
||||
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
|
||||
py.cleanup -e .swp -e .cache # also remove files with these extensions
|
||||
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
|
||||
py.cleanup -d # also remove empty directories
|
||||
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
|
||||
py.cleanup -n # dry run, only show what would be removed
|
||||
|
||||
- add a new option "py.test --funcargs" which shows available funcargs
|
||||
and their help strings (docstrings on their respective factory function)
|
||||
for a given test path
|
||||
|
||||
- display a short and concise traceback if a funcarg lookup fails
|
||||
|
||||
- early-load "conftest.py" files in non-dot first-level sub directories.
|
||||
allows to conveniently keep and access test-related options in a ``test``
|
||||
subdir and still add command line options.
|
||||
|
||||
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
|
||||
|
||||
- fix issue78: always call python-level teardown functions even if the
|
||||
according setup failed. This includes refinements for calling setup_module/class functions
|
||||
which will now only be called once instead of the previous behaviour where they'd be called
|
||||
multiple times if they raise an exception (including a Skipped exception). Any exception
|
||||
will be re-corded and associated with all tests in the according module/class scope.
|
||||
|
||||
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
|
||||
|
||||
- fix pdb debugging to be in the correct frame on raises-related errors
|
||||
|
||||
- update apipkg.py to fix an issue where recursive imports might
|
||||
unnecessarily break importing
|
||||
|
||||
- fix plugin links
|
||||
|
||||
Changes between 1.2 and 1.1.1
|
||||
=====================================
|
||||
|
||||
- moved dist/looponfailing from py.test core into a new
|
||||
separately released pytest-xdist plugin.
|
||||
|
||||
- new junitxml plugin: --junitxml=path will generate a junit style xml file
|
||||
which is processable e.g. by the Hudson CI system.
|
||||
|
||||
- new option: --genscript=path will generate a standalone py.test script
|
||||
which will not need any libraries installed. thanks to Ralf Schmitt.
|
||||
|
||||
- new option: --ignore will prevent specified path from collection.
|
||||
Can be specified multiple times.
|
||||
|
||||
- new option: --confcutdir=dir will make py.test only consider conftest
|
||||
files that are relative to the specified dir.
|
||||
|
||||
- new funcarg: "pytestconfig" is the pytest config object for access
|
||||
to command line args and can now be easily used in a test.
|
||||
|
||||
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
|
||||
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
|
||||
|
||||
- new "pytestconfig" funcarg allows access to test config object
|
||||
|
||||
- new "pytest_report_header" hook can return additional lines
|
||||
to be displayed at the header of a test run.
|
||||
|
||||
- (experimental) allow "py.test path::name1::name2::..." for pointing
|
||||
to a test within a test collection directly. This might eventually
|
||||
evolve as a full substitute to "-k" specifications.
|
||||
|
||||
- streamlined plugin loading: order is now as documented in
|
||||
customize.html: setuptools, ENV, commandline, conftest.
|
||||
also setuptools entry point names are turned to canonical namees ("pytest_*")
|
||||
|
||||
- automatically skip tests that need 'capfd' but have no os.dup
|
||||
|
||||
- allow pytest_generate_tests to be defined in classes as well
|
||||
|
||||
- deprecate usage of 'disabled' attribute in favour of pytestmark
|
||||
- deprecate definition of Directory, Module, Class and Function nodes
|
||||
in conftest.py files. Use pytest collect hooks instead.
|
||||
|
||||
- collection/item node specific runtest/collect hooks are only called exactly
|
||||
on matching conftest.py files, i.e. ones which are exactly below
|
||||
the filesystem path of an item
|
||||
|
||||
- change: the first pytest_collect_directory hook to return something
|
||||
will now prevent further hooks to be called.
|
||||
|
||||
- change: figleaf plugin now requires --figleaf to run. Also
|
||||
change its long command line options to be a bit shorter (see py.test -h).
|
||||
|
||||
- change: pytest doctest plugin is now enabled by default and has a
|
||||
new option --doctest-glob to set a pattern for file matches.
|
||||
|
||||
- change: remove internal py._* helper vars, only keep py._pydir
|
||||
|
||||
- robustify capturing to survive if custom pytest_runtest_setup
|
||||
code failed and prevented the capturing setup code from running.
|
||||
|
||||
- make py.test.* helpers provided by default plugins visible early -
|
||||
works transparently both for pydoc and for interactive sessions
|
||||
which will regularly see e.g. py.test.mark and py.test.importorskip.
|
||||
|
||||
- simplify internal plugin manager machinery
|
||||
- simplify internal collection tree by introducing a RootCollector node
|
||||
|
||||
- fix assert reinterpreation that sees a call containing "keyword=..."
|
||||
|
||||
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
|
||||
hooks on slaves during dist-testing, report module/session teardown
|
||||
hooks correctly.
|
||||
|
||||
- fix issue65: properly handle dist-testing if no
|
||||
execnet/py lib installed remotely.
|
||||
|
||||
- skip some install-tests if no execnet is available
|
||||
|
||||
- fix docs, fix internal bin/ script generation
|
||||
|
||||
|
||||
Changes between 1.1.1 and 1.1.0
|
||||
=====================================
|
||||
|
||||
- introduce automatic plugin registration via 'pytest11'
|
||||
entrypoints via setuptools' pkg_resources.iter_entry_points
|
||||
|
||||
- fix py.test dist-testing to work with execnet >= 1.0.0b4
|
||||
|
||||
- re-introduce py.test.cmdline.main() for better backward compatibility
|
||||
|
||||
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
|
||||
allow '%' in svn paths, make svnwc.update() default to interactive mode
|
||||
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
|
||||
|
||||
- refine distributed tarball to contain test and no pyc files
|
||||
|
||||
- try harder to have deprecation warnings for py.compat.* accesses
|
||||
report a correct location
|
||||
|
||||
Changes between 1.1.0 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* adjust and improve docs
|
||||
|
||||
* remove py.rest tool and internal namespace - it was
|
||||
never really advertised and can still be used with
|
||||
the old release if needed. If there is interest
|
||||
it could be revived into its own tool i guess.
|
||||
|
||||
* fix issue48 and issue59: raise an Error if the module
|
||||
from an imported test file does not seem to come from
|
||||
the filepath - avoids "same-name" confusion that has
|
||||
been reported repeatedly
|
||||
|
||||
* merged Ronny's nose-compatibility hacks: now
|
||||
nose-style setup_module() and setup() functions are
|
||||
supported
|
||||
|
||||
* introduce generalized py.test.mark function marking
|
||||
|
||||
* reshuffle / refine command line grouping
|
||||
|
||||
* deprecate parser.addgroup in favour of getgroup which creates option group
|
||||
|
||||
* add --report command line option that allows to control showing of skipped/xfailed sections
|
||||
|
||||
* generalized skipping: a new way to mark python functions with skipif or xfail
|
||||
at function, class and modules level based on platform or sys-module attributes.
|
||||
|
||||
* extend py.test.mark decorator to allow for positional args
|
||||
|
||||
* introduce and test "py.cleanup -d" to remove empty directories
|
||||
|
||||
* fix issue #59 - robustify unittest test collection
|
||||
|
||||
* make bpython/help interaction work by adding an __all__ attribute
|
||||
to ApiModule, cleanup initpkg
|
||||
|
||||
* use MIT license for pylib, add some contributors
|
||||
|
||||
* remove py.execnet code and substitute all usages with 'execnet' proper
|
||||
|
||||
* fix issue50 - cached_setup now caches more to expectations
|
||||
for test functions with multiple arguments.
|
||||
|
||||
* merge Jarko's fixes, issue #45 and #46
|
||||
|
||||
* add the ability to specify a path for py.lookup to search in
|
||||
|
||||
* fix a funcarg cached_setup bug probably only occuring
|
||||
in distributed testing and "module" scope with teardown.
|
||||
|
||||
* many fixes and changes for making the code base python3 compatible,
|
||||
many thanks to Benjamin Peterson for helping with this.
|
||||
|
||||
* consolidate builtins implementation to be compatible with >=2.3,
|
||||
add helpers to ease keeping 2 and 3k compatible code
|
||||
|
||||
* deprecate py.compat.doctest|subprocess|textwrap|optparse
|
||||
|
||||
* deprecate py.magic.autopath, remove py/magic directory
|
||||
|
||||
* move pytest assertion handling to py/code and a pytest_assertion
|
||||
plugin, add "--no-assert" option, deprecate py.magic namespaces
|
||||
in favour of (less) py.code ones.
|
||||
|
||||
* consolidate and cleanup py/code classes and files
|
||||
|
||||
* cleanup py/misc, move tests to bin-for-dist
|
||||
|
||||
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
|
||||
|
||||
* consolidate py.log implementation, remove old approach.
|
||||
|
||||
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
||||
text/unicode and byte-streams (uses underlying standard lib io.*
|
||||
if available)
|
||||
|
||||
* make py.unittest_convert helper script available which converts "unittest.py"
|
||||
style files into the simpler assert/direct-test-classes py.test/nosetests
|
||||
style. The script was written by Laura Creighton.
|
||||
|
||||
* simplified internal localpath implementation
|
||||
|
||||
Changes between 1.0.1 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* fixing packaging issues, triggered by fedora redhat packaging,
|
||||
also added doc, examples and contrib dirs to the tarball.
|
||||
|
||||
* added a documentation link to the new django plugin.
|
||||
|
||||
Changes between 1.0.0 and 1.0.1
|
||||
=====================================
|
||||
|
||||
* added a 'pytest_nose' plugin which handles nose.SkipTest,
|
||||
nose-style function/method/generator setup/teardown and
|
||||
tries to report functions correctly.
|
||||
|
||||
* capturing of unicode writes or encoded strings to sys.stdout/err
|
||||
work better, also terminalwriting was adapted and somewhat
|
||||
unified between windows and linux.
|
||||
|
||||
* improved documentation layout and content a lot
|
||||
|
||||
* added a "--help-config" option to show conftest.py / ENV-var names for
|
||||
all longopt cmdline options, and some special conftest.py variables.
|
||||
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
|
||||
|
||||
* fix issue #27: better reporting on non-collectable items given on commandline
|
||||
(e.g. pyc files)
|
||||
|
||||
* fix issue #33: added --version flag (thanks Benjamin Peterson)
|
||||
|
||||
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
|
||||
|
||||
* "Test" prefixed classes are *not* collected by default anymore if they
|
||||
have an __init__ method
|
||||
|
||||
* monkeypatch setenv() now accepts a "prepend" parameter
|
||||
|
||||
* improved reporting of collection error tracebacks
|
||||
|
||||
* simplified multicall mechanism and plugin architecture,
|
||||
renamed some internal methods and argnames
|
||||
|
||||
Changes between 1.0.0b9 and 1.0.0
|
||||
=====================================
|
||||
|
||||
* more terse reporting try to show filesystem path relatively to current dir
|
||||
* improve xfail output a bit
|
||||
|
||||
Changes between 1.0.0b8 and 1.0.0b9
|
||||
=====================================
|
||||
|
||||
* cleanly handle and report final teardown of test setup
|
||||
|
||||
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
|
||||
(thanks Wouter Vanden Hove)
|
||||
|
||||
* setup/teardown or collection problems now show as ERRORs
|
||||
or with big "E"'s in the progress lines. they are reported
|
||||
and counted separately.
|
||||
|
||||
* dist-testing: properly handle test items that get locally
|
||||
collected but cannot be collected on the remote side - often
|
||||
due to platform/dependency reasons
|
||||
|
||||
* simplified py.test.mark API - see keyword plugin documentation
|
||||
|
||||
* integrate better with logging: capturing now by default captures
|
||||
test functions and their immediate setup/teardown in a single stream
|
||||
|
||||
* capsys and capfd funcargs now have a readouterr() and a close() method
|
||||
(underlyingly py.io.StdCapture/FD objects are used which grew a
|
||||
readouterr() method as well to return snapshots of captured out/err)
|
||||
|
||||
* make assert-reinterpretation work better with comparisons not
|
||||
returning bools (reported with numpy from thanks maciej fijalkowski)
|
||||
|
||||
* reworked per-test output capturing into the pytest_iocapture.py plugin
|
||||
and thus removed capturing code from config object
|
||||
|
||||
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
|
||||
|
||||
|
||||
Changes between 1.0.0b7 and 1.0.0b8
|
||||
=====================================
|
||||
|
||||
* pytest_unittest-plugin is now enabled by default
|
||||
|
||||
* introduced pytest_keyboardinterrupt hook and
|
||||
refined pytest_sessionfinish hooked, added tests.
|
||||
|
||||
* workaround a buggy logging module interaction ("closing already closed
|
||||
files"). Thanks to Sridhar Ratnakumar for triggering.
|
||||
|
||||
* if plugins use "py.test.importorskip" for importing
|
||||
a dependency only a warning will be issued instead
|
||||
of exiting the testing process.
|
||||
|
||||
* many improvements to docs:
|
||||
- refined funcargs doc , use the term "factory" instead of "provider"
|
||||
- added a new talk/tutorial doc page
|
||||
- better download page
|
||||
- better plugin docstrings
|
||||
- added new plugins page and automatic doc generation script
|
||||
|
||||
* fixed teardown problem related to partially failing funcarg setups
|
||||
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
|
||||
always invoked even if the "pytest_runtest_setup" failed.
|
||||
|
||||
* tweaked doctest output for docstrings in py modules,
|
||||
thanks Radomir.
|
||||
|
||||
Changes between 1.0.0b3 and 1.0.0b7
|
||||
=============================================
|
||||
|
||||
* renamed py.test.xfail back to py.test.mark.xfail to avoid
|
||||
two ways to decorate for xfail
|
||||
|
||||
* re-added py.test.mark decorator for setting keywords on functions
|
||||
(it was actually documented so removing it was not nice)
|
||||
|
||||
* remove scope-argument from request.addfinalizer() because
|
||||
request.cached_setup has the scope arg. TOOWTDI.
|
||||
|
||||
* perform setup finalization before reporting failures
|
||||
|
||||
* apply modified patches from Andreas Kloeckner to allow
|
||||
test functions to have no func_code (#22) and to make
|
||||
"-k" and function keywords work (#20)
|
||||
|
||||
* apply patch from Daniel Peolzleithner (issue #23)
|
||||
|
||||
* resolve issue #18, multiprocessing.Manager() and
|
||||
redirection clash
|
||||
|
||||
* make __name__ == "__channelexec__" for remote_exec code
|
||||
|
||||
Changes between 1.0.0b1 and 1.0.0b3
|
||||
=============================================
|
||||
|
||||
* plugin classes are removed: one now defines
|
||||
hooks directly in conftest.py or global pytest_*.py
|
||||
files.
|
||||
|
||||
* added new pytest_namespace(config) hook that allows
|
||||
to inject helpers directly to the py.test.* namespace.
|
||||
|
||||
* documented and refined many hooks
|
||||
|
||||
* added new style of generative tests via
|
||||
pytest_generate_tests hook that integrates
|
||||
well with function arguments.
|
||||
|
||||
|
||||
Changes between 0.9.2 and 1.0.0b1
|
||||
=============================================
|
||||
|
||||
* introduced new "funcarg" setup method,
|
||||
see doc/test/funcarg.txt
|
||||
|
||||
* introduced plugin architecuture and many
|
||||
new py.test plugins, see
|
||||
doc/test/plugins.txt
|
||||
|
||||
* teardown_method is now guaranteed to get
|
||||
called after a test method has run.
|
||||
|
||||
* new method: py.test.importorskip(mod,minversion)
|
||||
will either import or call py.test.skip()
|
||||
|
||||
* completely revised internal py.test architecture
|
||||
|
||||
* new py.process.ForkedFunc object allowing to
|
||||
fork execution of a function to a sub process
|
||||
and getting a result back.
|
||||
|
||||
XXX lots of things missing here XXX
|
||||
|
||||
Changes between 0.9.1 and 0.9.2
|
||||
===============================
|
||||
|
||||
* refined installation and metadata, created new setup.py,
|
||||
now based on setuptools/ez_setup (thanks to Ralf Schmitt
|
||||
for his support).
|
||||
|
||||
* improved the way of making py.* scripts available in
|
||||
windows environments, they are now added to the
|
||||
Scripts directory as ".cmd" files.
|
||||
|
||||
* py.path.svnwc.status() now is more complete and
|
||||
uses xml output from the 'svn' command if available
|
||||
(Guido Wesdorp)
|
||||
|
||||
* fix for py.path.svn* to work with svn 1.5
|
||||
(Chris Lamb)
|
||||
|
||||
* fix path.relto(otherpath) method on windows to
|
||||
use normcase for checking if a path is relative.
|
||||
|
||||
* py.test's traceback is better parseable from editors
|
||||
(follows the filenames:LINENO: MSG convention)
|
||||
(thanks to Osmo Salomaa)
|
||||
|
||||
* fix to javascript-generation, "py.test --runbrowser"
|
||||
should work more reliably now
|
||||
|
||||
* removed previously accidentally added
|
||||
py.test.broken and py.test.notimplemented helpers.
|
||||
|
||||
* there now is a py.__version__ attribute
|
||||
|
||||
Changes between 0.9.0 and 0.9.1
|
||||
===============================
|
||||
|
||||
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
|
||||
serve as a reference for developers.
|
||||
|
||||
* allowing + signs in py.path.svn urls [39106]
|
||||
* fixed support for Failed exceptions without excinfo in py.test [39340]
|
||||
* added support for killing processes for Windows (as well as platforms that
|
||||
support os.kill) in py.misc.killproc [39655]
|
||||
* added setup/teardown for generative tests to py.test [40702]
|
||||
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
|
||||
* fixed problem with calling .remove() on wcpaths of non-versioned files in
|
||||
py.path [44248]
|
||||
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
|
||||
* fail to run greenlet tests when pypy is available, but without stackless
|
||||
[45294]
|
||||
* small fixes in rsession tests [45295]
|
||||
* fixed issue with 2.5 type representations in py.test [45483, 45484]
|
||||
* made that internal reporting issues displaying is done atomically in py.test
|
||||
[45518]
|
||||
* made that non-existing files are igored by the py.lookup script [45519]
|
||||
* improved exception name creation in py.test [45535]
|
||||
* made that less threads are used in execnet [merge in 45539]
|
||||
* removed lock required for atomical reporting issue displaying in py.test
|
||||
[45545]
|
||||
* removed globals from execnet [45541, 45547]
|
||||
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
|
||||
get called in 2.5 (py.execnet) [45548]
|
||||
* fixed bug in joining threads in py.execnet's servemain [45549]
|
||||
* refactored py.test.rsession tests to not rely on exact output format anymore
|
||||
[45646]
|
||||
* using repr() on test outcome [45647]
|
||||
* added 'Reason' classes for py.test.skip() [45648, 45649]
|
||||
* killed some unnecessary sanity check in py.test.collect [45655]
|
||||
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
|
||||
usable by Administrators [45901]
|
||||
* added support for locking and non-recursive commits to py.path.svnwc [45994]
|
||||
* locking files in py.execnet to prevent CPython from segfaulting [46010]
|
||||
* added export() method to py.path.svnurl
|
||||
* fixed -d -x in py.test [47277]
|
||||
* fixed argument concatenation problem in py.path.svnwc [49423]
|
||||
* restore py.test behaviour that it exits with code 1 when there are failures
|
||||
[49974]
|
||||
* don't fail on html files that don't have an accompanying .txt file [50606]
|
||||
* fixed 'utestconvert.py < input' [50645]
|
||||
* small fix for code indentation in py.code.source [50755]
|
||||
* fix _docgen.py documentation building [51285]
|
||||
* improved checks for source representation of code blocks in py.test [51292]
|
||||
* added support for passing authentication to py.path.svn* objects [52000,
|
||||
52001]
|
||||
* removed sorted() call for py.apigen tests in favour of [].sort() to support
|
||||
Python 2.3 [52481]
|
||||
@@ -5,10 +5,12 @@ Release notes
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 2
|
||||
|
||||
announce/release-1.0.2
|
||||
announce/release-1.0.1
|
||||
announce/release-1.0.0
|
||||
announce/release-0.9.2
|
||||
announce/release-0.9.0
|
||||
.. include: release-1.1.0
|
||||
.. include: release-1.0.2
|
||||
|
||||
release-1.0.1
|
||||
release-1.0.0
|
||||
release-0.9.2
|
||||
release-0.9.0
|
||||
|
||||
63
doc/bin.txt
63
doc/bin.txt
@@ -1,22 +1,40 @@
|
||||
======================
|
||||
``py/bin/`` scripts
|
||||
pylib scripts
|
||||
======================
|
||||
|
||||
The py-lib contains some scripts, most of which are
|
||||
small ones (apart from ``py.test``) that help during
|
||||
the python development process. If working
|
||||
from a svn-checkout of py lib you may add ``py/bin``
|
||||
to your shell ``PATH`` which should make the scripts
|
||||
available on your command prompt.
|
||||
The pylib installs several scripts to support testing and (python)
|
||||
development. If working from a checkout you may also add ``bin`` to
|
||||
your ``PATH`` environment variable which makes the scripts available on
|
||||
your shell prompt.
|
||||
|
||||
``py.test``
|
||||
===========
|
||||
``py.test`` and ``py.test-$VERSION``
|
||||
============================================
|
||||
|
||||
The ``py.test`` executable is the main entry point into the py-lib testing tool,
|
||||
see the `py.test documentation`_.
|
||||
The ``py.test`` executable is the main tool that the py lib offers;
|
||||
in fact most code in the py lib is geared towards supporting the
|
||||
testing process. See the `py.test documentation`_ for extensive
|
||||
documentation. The ``py.test-$VERSION`` is the same script with
|
||||
an interpreter specific suffix appended to make
|
||||
several versions of py.test for using specific interpreters
|
||||
accessible:
|
||||
|
||||
* CPython2.4: py.test-2.4
|
||||
* CPython2.5: py.test-2.5
|
||||
* ...
|
||||
* CPython3.1: py.test-3.1
|
||||
* Jython-2.5.1: py.test-jython
|
||||
* pypy-$SUFFIX: py.test-pypy-$SUFFIX
|
||||
|
||||
.. _`py.test documentation`: test/index.html
|
||||
|
||||
``py.which`` and ``py.which-$VERSION``
|
||||
=========================================
|
||||
|
||||
Usage: ``py.which modulename``
|
||||
|
||||
Print the ``__file__`` of the module that is imported via ``import modulename``.
|
||||
The version-suffix is the same as with ``py.test`` above.
|
||||
|
||||
``py.cleanup``
|
||||
==============
|
||||
|
||||
@@ -26,7 +44,6 @@ Delete pyc file recursively, starting from ``PATH`` (which defaults to the
|
||||
current working directory). Don't follow links and don't recurse into
|
||||
directories with a ".".
|
||||
|
||||
|
||||
``py.countloc``
|
||||
===============
|
||||
|
||||
@@ -46,25 +63,3 @@ Looks recursively at Python files for a ``SEARCH_STRING``, starting from the
|
||||
present working directory. Prints the line, with the filename and line-number
|
||||
prepended.
|
||||
|
||||
``py.rest``
|
||||
===========
|
||||
|
||||
Usage: ``py.rest [PATHS] [options]``
|
||||
|
||||
[deprecated in 1.0, will likely be separated]
|
||||
Loot recursively for .txt files starting from ``PATHS`` and convert them to
|
||||
html using docutils or to pdf files, if the ``--pdf`` option is used. For
|
||||
conversion to PDF you will need several command line tools, on Ubuntu Linux
|
||||
this is **texlive** and **texlive-extra-utils**.
|
||||
|
||||
``py.rest`` has some extra features over rst2html (which is shipped with
|
||||
docutils). Most of these are still experimental, the one which is most likely
|
||||
not going to change is the `graphviz`_ directive. With that you can embed .dot
|
||||
files into your document and have them be converted to png (when outputting
|
||||
html) and to eps (when outputting pdf). Otherwise the directive works mostly
|
||||
like the image directive::
|
||||
|
||||
.. graphviz:: example.dot
|
||||
:scale: 90
|
||||
|
||||
.. _`graphviz`: http://www.graphviz.org
|
||||
|
||||
@@ -1,263 +1,2 @@
|
||||
Changes between 1.0.1 and 1.0.2
|
||||
=====================================
|
||||
|
||||
* fixing packaging issues, triggered by fedora redhat packaging,
|
||||
also added doc, examples and contrib dirs to the tarball.
|
||||
|
||||
* added a documentation link to the new pytest_django plugin.
|
||||
|
||||
Changes between 1.0.0 and 1.0.1
|
||||
=====================================
|
||||
|
||||
* added a 'pytest_nose' plugin which handles nose.SkipTest,
|
||||
nose-style function/method/generator setup/teardown and
|
||||
tries to report functions correctly.
|
||||
|
||||
* capturing of unicode writes or encoded strings to sys.stdout/err
|
||||
work better, also terminalwriting was adapted and somewhat
|
||||
unified between windows and linux.
|
||||
|
||||
* improved documentation layout and content a lot
|
||||
|
||||
* added a "--help-config" option to show conftest.py / ENV-var names for
|
||||
all longopt cmdline options, and some special conftest.py variables.
|
||||
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
|
||||
|
||||
* fix issue #27: better reporting on non-collectable items given on commandline
|
||||
(e.g. pyc files)
|
||||
|
||||
* fix issue #33: added --version flag (thanks Benjamin Peterson)
|
||||
|
||||
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
|
||||
|
||||
* "Test" prefixed classes are *not* collected by default anymore if they
|
||||
have an __init__ method
|
||||
|
||||
* monkeypatch setenv() now accepts a "prepend" parameter
|
||||
|
||||
* improved reporting of collection error tracebacks
|
||||
|
||||
* simplified multicall mechanism and plugin architecture,
|
||||
renamed some internal methods and argnames
|
||||
|
||||
Changes between 1.0.0b9 and 1.0.0
|
||||
=====================================
|
||||
|
||||
* more terse reporting try to show filesystem path relatively to current dir
|
||||
* improve xfail output a bit
|
||||
|
||||
Changes between 1.0.0b8 and 1.0.0b9
|
||||
=====================================
|
||||
|
||||
* cleanly handle and report final teardown of test setup
|
||||
|
||||
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
|
||||
(thanks Wouter Vanden Hove)
|
||||
|
||||
* setup/teardown or collection problems now show as ERRORs
|
||||
or with big "E"'s in the progress lines. they are reported
|
||||
and counted separately.
|
||||
|
||||
* dist-testing: properly handle test items that get locally
|
||||
collected but cannot be collected on the remote side - often
|
||||
due to platform/dependency reasons
|
||||
|
||||
* simplified py.test.mark API - see keyword plugin documentation
|
||||
|
||||
* integrate better with logging: capturing now by default captures
|
||||
test functions and their immediate setup/teardown in a single stream
|
||||
|
||||
* capsys and capfd funcargs now have a readouterr() and a close() method
|
||||
(underlyingly py.io.StdCapture/FD objects are used which grew a
|
||||
readouterr() method as well to return snapshots of captured out/err)
|
||||
|
||||
* make assert-reinterpretation work better with comparisons not
|
||||
returning bools (reported with numpy from thanks maciej fijalkowski)
|
||||
|
||||
* reworked per-test output capturing into the pytest_iocapture.py plugin
|
||||
and thus removed capturing code from config object
|
||||
|
||||
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
|
||||
|
||||
|
||||
Changes between 1.0.0b7 and 1.0.0b8
|
||||
=====================================
|
||||
|
||||
* pytest_unittest-plugin is now enabled by default
|
||||
|
||||
* introduced pytest_keyboardinterrupt hook and
|
||||
refined pytest_sessionfinish hooked, added tests.
|
||||
|
||||
* workaround a buggy logging module interaction ("closing already closed
|
||||
files"). Thanks to Sridhar Ratnakumar for triggering.
|
||||
|
||||
* if plugins use "py.test.importorskip" for importing
|
||||
a dependency only a warning will be issued instead
|
||||
of exiting the testing process.
|
||||
|
||||
* many improvements to docs:
|
||||
- refined funcargs doc , use the term "factory" instead of "provider"
|
||||
- added a new talk/tutorial doc page
|
||||
- better download page
|
||||
- better plugin docstrings
|
||||
- added new plugins page and automatic doc generation script
|
||||
|
||||
* fixed teardown problem related to partially failing funcarg setups
|
||||
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
|
||||
always invoked even if the "pytest_runtest_setup" failed.
|
||||
|
||||
* tweaked doctest output for docstrings in py modules,
|
||||
thanks Radomir.
|
||||
|
||||
Changes between 1.0.0b3 and 1.0.0b7
|
||||
=============================================
|
||||
|
||||
* renamed py.test.xfail back to py.test.mark.xfail to avoid
|
||||
two ways to decorate for xfail
|
||||
|
||||
* re-added py.test.mark decorator for setting keywords on functions
|
||||
(it was actually documented so removing it was not nice)
|
||||
|
||||
* remove scope-argument from request.addfinalizer() because
|
||||
request.cached_setup has the scope arg. TOOWTDI.
|
||||
|
||||
* perform setup finalization before reporting failures
|
||||
|
||||
* apply modified patches from Andreas Kloeckner to allow
|
||||
test functions to have no func_code (#22) and to make
|
||||
"-k" and function keywords work (#20)
|
||||
|
||||
* apply patch from Daniel Peolzleithner (issue #23)
|
||||
|
||||
* resolve issue #18, multiprocessing.Manager() and
|
||||
redirection clash
|
||||
|
||||
* make __name__ == "__channelexec__" for remote_exec code
|
||||
|
||||
Changes between 1.0.0b1 and 1.0.0b3
|
||||
=============================================
|
||||
|
||||
* plugin classes are removed: one now defines
|
||||
hooks directly in conftest.py or global pytest_*.py
|
||||
files.
|
||||
|
||||
* added new pytest_namespace(config) hook that allows
|
||||
to inject helpers directly to the py.test.* namespace.
|
||||
|
||||
* documented and refined many hooks
|
||||
|
||||
* added new style of generative tests via
|
||||
pytest_generate_tests hook that integrates
|
||||
well with function arguments.
|
||||
|
||||
|
||||
Changes between 0.9.2 and 1.0.0b1
|
||||
=============================================
|
||||
|
||||
* introduced new "funcarg" setup method,
|
||||
see doc/test/funcarg.txt
|
||||
|
||||
* introduced plugin architecuture and many
|
||||
new py.test plugins, see
|
||||
doc/test/plugins.txt
|
||||
|
||||
* teardown_method is now guaranteed to get
|
||||
called after a test method has run.
|
||||
|
||||
* new method: py.test.importorskip(mod,minversion)
|
||||
will either import or call py.test.skip()
|
||||
|
||||
* completely revised internal py.test architecture
|
||||
|
||||
* new py.process.ForkedFunc object allowing to
|
||||
fork execution of a function to a sub process
|
||||
and getting a result back.
|
||||
|
||||
XXX lots of things missing here XXX
|
||||
|
||||
Changes between 0.9.1 and 0.9.2
|
||||
===============================
|
||||
|
||||
* refined installation and metadata, created new setup.py,
|
||||
now based on setuptools/ez_setup (thanks to Ralf Schmitt
|
||||
for his support).
|
||||
|
||||
* improved the way of making py.* scripts available in
|
||||
windows environments, they are now added to the
|
||||
Scripts directory as ".cmd" files.
|
||||
|
||||
* py.path.svnwc.status() now is more complete and
|
||||
uses xml output from the 'svn' command if available
|
||||
(Guido Wesdorp)
|
||||
|
||||
* fix for py.path.svn* to work with svn 1.5
|
||||
(Chris Lamb)
|
||||
|
||||
* fix path.relto(otherpath) method on windows to
|
||||
use normcase for checking if a path is relative.
|
||||
|
||||
* py.test's traceback is better parseable from editors
|
||||
(follows the filenames:LINENO: MSG convention)
|
||||
(thanks to Osmo Salomaa)
|
||||
|
||||
* fix to javascript-generation, "py.test --runbrowser"
|
||||
should work more reliably now
|
||||
|
||||
* removed previously accidentally added
|
||||
py.test.broken and py.test.notimplemented helpers.
|
||||
|
||||
* there now is a py.__version__ attribute
|
||||
|
||||
Changes between 0.9.0 and 0.9.1
|
||||
===============================
|
||||
|
||||
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
|
||||
serve as a reference for developers.
|
||||
|
||||
* allowing + signs in py.path.svn urls [39106]
|
||||
* fixed support for Failed exceptions without excinfo in py.test [39340]
|
||||
* added support for killing processes for Windows (as well as platforms that
|
||||
support os.kill) in py.misc.killproc [39655]
|
||||
* added setup/teardown for generative tests to py.test [40702]
|
||||
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
|
||||
* fixed problem with calling .remove() on wcpaths of non-versioned files in
|
||||
py.path [44248]
|
||||
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
|
||||
* fail to run greenlet tests when pypy is available, but without stackless
|
||||
[45294]
|
||||
* small fixes in rsession tests [45295]
|
||||
* fixed issue with 2.5 type representations in py.test [45483, 45484]
|
||||
* made that internal reporting issues displaying is done atomically in py.test
|
||||
[45518]
|
||||
* made that non-existing files are igored by the py.lookup script [45519]
|
||||
* improved exception name creation in py.test [45535]
|
||||
* made that less threads are used in execnet [merge in 45539]
|
||||
* removed lock required for atomical reporting issue displaying in py.test
|
||||
[45545]
|
||||
* removed globals from execnet [45541, 45547]
|
||||
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
|
||||
get called in 2.5 (py.execnet) [45548]
|
||||
* fixed bug in joining threads in py.execnet's servemain [45549]
|
||||
* refactored py.test.rsession tests to not rely on exact output format anymore
|
||||
[45646]
|
||||
* using repr() on test outcome [45647]
|
||||
* added 'Reason' classes for py.test.skip() [45648, 45649]
|
||||
* killed some unnecessary sanity check in py.test.collect [45655]
|
||||
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
|
||||
usable by Administrators [45901]
|
||||
* added support for locking and non-recursive commits to py.path.svnwc [45994]
|
||||
* locking files in py.execnet to prevent CPython from segfaulting [46010]
|
||||
* added export() method to py.path.svnurl
|
||||
* fixed -d -x in py.test [47277]
|
||||
* fixed argument concatenation problem in py.path.svnwc [49423]
|
||||
* restore py.test behaviour that it exits with code 1 when there are failures
|
||||
[49974]
|
||||
* don't fail on html files that don't have an accompanying .txt file [50606]
|
||||
* fixed 'utestconvert.py < input' [50645]
|
||||
* small fix for code indentation in py.code.source [50755]
|
||||
* fix _docgen.py documentation building [51285]
|
||||
* improved checks for source representation of code blocks in py.test [51292]
|
||||
* added support for passing authentication to py.path.svn* objects [52000,
|
||||
52001]
|
||||
* removed sorted() call for py.apigen tests in favour of [].sort() to support
|
||||
Python 2.3 [52481]
|
||||
.. include:: ../CHANGELOG
|
||||
|
||||
41
doc/code.txt
41
doc/code.txt
@@ -2,35 +2,33 @@
|
||||
py.code: higher level python code and introspection objects
|
||||
================================================================================
|
||||
|
||||
The :api:`py.code` part of the pylib contains some functionality to help
|
||||
The ``py.code`` part of the pylib contains some functionality to help
|
||||
dealing with Python code objects. Even though working with Python's internal
|
||||
code objects (as found on frames and callables) can be very powerful, it's
|
||||
usually also quite cumbersome, because the API provided by core Python is
|
||||
relatively low level and not very accessible.
|
||||
|
||||
The :api:`py.code` library tries to simplify accessing the code objects as well
|
||||
The ``py.code`` library tries to simplify accessing the code objects as well
|
||||
as creating them. There is a small set of interfaces a user needs to deal with,
|
||||
all nicely bundled together, and with a rich set of 'Pythonic' functionality.
|
||||
|
||||
source: :source:`py/code/`
|
||||
|
||||
Contents of the library
|
||||
=======================
|
||||
|
||||
Every object in the :api:`py.code` library wraps a code Python object related
|
||||
to code objects, source code, frames and tracebacks: the :api:`py.code.Code`
|
||||
class wraps code objects, :api:`py.code.Source` source snippets,
|
||||
:api:`py.code.Traceback` exception tracebacks, :api:`py.code.Frame` frame
|
||||
objects (as found in e.g. tracebacks) and :api:`py.code.ExceptionInfo` the
|
||||
Every object in the ``py.code`` library wraps a code Python object related
|
||||
to code objects, source code, frames and tracebacks: the ``py.code.Code``
|
||||
class wraps code objects, ``py.code.Source`` source snippets,
|
||||
``py.code.Traceback` exception tracebacks, ``py.code.Frame`` frame
|
||||
objects (as found in e.g. tracebacks) and ``py.code.ExceptionInfo`` the
|
||||
tuple provided by sys.exc_info() (containing exception and traceback
|
||||
information when an exception occurs). Also in the library is a helper function
|
||||
:api:`py.code.compile()` that provides the same functionality as Python's
|
||||
``py.code.compile()`` that provides the same functionality as Python's
|
||||
built-in 'compile()' function, but returns a wrapped code object.
|
||||
|
||||
The wrappers
|
||||
============
|
||||
|
||||
:api:`py.code.Code`
|
||||
``py.code.Code``
|
||||
-------------------
|
||||
|
||||
Code objects are instantiated with a code object or a callable as argument,
|
||||
@@ -46,11 +44,9 @@ A quick example::
|
||||
>>> isinstance(c.source(), py.code.Source)
|
||||
True
|
||||
>>> str(c.source()).split('\n')[0]
|
||||
"def read(self, mode='rb'):"
|
||||
"def read(self, mode='r'):"
|
||||
|
||||
source: :source:`py/code/code.py`
|
||||
|
||||
:api:`py.code.Source`
|
||||
``py.code.Source``
|
||||
---------------------
|
||||
|
||||
Source objects wrap snippets of Python source code, providing a simple yet
|
||||
@@ -71,9 +67,8 @@ Example::
|
||||
>>> str(sub).strip() # XXX why is the strip() required?!?
|
||||
'print "foo"'
|
||||
|
||||
source: :source:`py/code/source.py`
|
||||
|
||||
:api:`py.code.Traceback`
|
||||
``py.code.Traceback``
|
||||
------------------------
|
||||
|
||||
Tracebacks are usually not very easy to examine, you need to access certain
|
||||
@@ -97,15 +92,13 @@ Example::
|
||||
>>> str(first.statement).strip().startswith('raise ValueError')
|
||||
True
|
||||
|
||||
source: :source:`py/code/traceback2.py`
|
||||
|
||||
:api:`py.code.Frame`
|
||||
``py.code.Frame``
|
||||
--------------------
|
||||
|
||||
Frame wrappers are used in :api:`py.code.Traceback` items, and will usually not
|
||||
Frame wrappers are used in ``py.code.Traceback`` items, and will usually not
|
||||
directly be instantiated. They provide some nice methods to evaluate code
|
||||
'inside' the frame (using the frame's local variables), get to the underlying
|
||||
code (frames have a code attribute that points to a :api:`py.code.Code` object)
|
||||
code (frames have a code attribute that points to a ``py.code.Code`` object)
|
||||
and examine the arguments.
|
||||
|
||||
Example (using the 'first' TracebackItem instance created above)::
|
||||
@@ -113,12 +106,12 @@ Example (using the 'first' TracebackItem instance created above)::
|
||||
>>> frame = first.frame
|
||||
>>> isinstance(frame.code, py.code.Code)
|
||||
True
|
||||
>>> isinstance(frame.eval('self'), py.__.path.local.local.LocalPath)
|
||||
>>> isinstance(frame.eval('self'), py.path.local)
|
||||
True
|
||||
>>> [namevalue[0] for namevalue in frame.getargs()]
|
||||
['cls', 'path']
|
||||
|
||||
:api:`py.code.ExceptionInfo`
|
||||
``py.code.ExceptionInfo``
|
||||
----------------------------
|
||||
|
||||
A wrapper around the tuple returned by sys.exc_info() (will call sys.exc_info()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import py
|
||||
from py.__.misc.rest import convert_rest_html, strip_html_header
|
||||
from py.__.misc.difftime import worded_time
|
||||
|
||||
from py._plugin.pytest_restdoc import convert_rest_html, strip_html_header
|
||||
|
||||
html = py.xml.html
|
||||
|
||||
@@ -57,24 +57,24 @@ pageTracker._trackPageview();
|
||||
|
||||
def fill_menubar(self):
|
||||
items = [
|
||||
self.a_docref("install", "download.html"),
|
||||
self.a_docref("contact", "contact.html"),
|
||||
self.a_docref("changelog", "changelog.html"),
|
||||
self.a_docref("faq", "faq.html"),
|
||||
self.a_docref("INSTALL", "install.html"),
|
||||
self.a_docref("CONTACT", "contact.html"),
|
||||
self.a_docref("CHANGELOG", "changelog.html"),
|
||||
self.a_docref("FAQ", "faq.html"),
|
||||
html.div(
|
||||
html.h3("py.test:"),
|
||||
self.a_docref("doc index", "test/index.html"),
|
||||
self.a_docref("features", "test/features.html"),
|
||||
self.a_docref("quickstart", "test/quickstart.html"),
|
||||
self.a_docref("tutorials", "test/talks.html"),
|
||||
self.a_docref("plugins", "test/plugin/index.html"),
|
||||
self.a_docref("funcargs", "test/funcargs.html"),
|
||||
self.a_docref("customize", "test/customize.html"),
|
||||
self.a_docref("Index", "test/index.html"),
|
||||
self.a_docref("Quickstart", "test/quickstart.html"),
|
||||
self.a_docref("Features", "test/features.html"),
|
||||
self.a_docref("Funcargs", "test/funcargs.html"),
|
||||
self.a_docref("Plugins", "test/plugin/index.html"),
|
||||
self.a_docref("Customize", "test/customize.html"),
|
||||
self.a_docref("Tutorials", "test/talks.html"),
|
||||
self.a_href("hudson-tests", "http://hudson.testrun.org")
|
||||
),
|
||||
html.div(
|
||||
html.h3("supporting APIs:"),
|
||||
self.a_docref("pylib index", "index.html"),
|
||||
self.a_docref("py.execnet", "execnet.html"),
|
||||
self.a_docref("Index", "index.html"),
|
||||
self.a_docref("py.path", "path.html"),
|
||||
self.a_docref("py.code", "code.html"),
|
||||
)
|
||||
@@ -86,9 +86,10 @@ pageTracker._trackPageview();
|
||||
self.menubar = html.div(id=css.menubar, *[
|
||||
html.div(item) for item in items])
|
||||
version = py.version
|
||||
announcelink = self.a_docref("%s ANN" % version,
|
||||
"announce/release-%s.html" %(version,))
|
||||
self.menubar.insert(0,
|
||||
html.div("%s" % (py.version), style="font-style: italic;")
|
||||
)
|
||||
html.div(announcelink))
|
||||
#self.a_href("%s-%s" % (self.title, py.version),
|
||||
# "http://pypi.python.org/pypi/py/%s" % version,
|
||||
#id="versioninfo",
|
||||
@@ -149,7 +150,7 @@ def getrealname(username):
|
||||
|
||||
|
||||
class Project:
|
||||
mydir = py.magic.autopath().dirpath()
|
||||
mydir = py.path.local(__file__).dirpath()
|
||||
title = "py lib"
|
||||
prefix_title = "" # we have a logo already containing "py lib"
|
||||
encoding = 'latin1'
|
||||
@@ -286,5 +287,3 @@ def relpath(p1, p2, sep=os.path.sep, back='..', normalize=True):
|
||||
if tolist_diff:
|
||||
return sep.join([back,]*(backcount-1) + tolist_diff)
|
||||
return sep.join([back,]*(backcount) + tolist[commonindex:])
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#XXX make work: excludedirs = ['_build']
|
||||
import py
|
||||
#py.test.importorskip("pygments")
|
||||
pytest_plugins = ['pytest_restdoc']
|
||||
rsyncdirs = ['.']
|
||||
|
||||
collect_ignore = ['test/attic.txt']
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
if item.fspath.ext == ".txt":
|
||||
py.test.importorskip("pygments") # for raising an error
|
||||
|
||||
18
doc/download.html
Normal file
18
doc/download.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content=" 1 ; URL=install.html" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("UA-7597274-3");
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
130
doc/download.txt
130
doc/download.txt
@@ -1,130 +0,0 @@
|
||||
..
|
||||
==============
|
||||
Downloading
|
||||
==============
|
||||
|
||||
.. _`PyPI project page`: http://pypi.python.org/pypi/py/
|
||||
|
||||
Latest Release, see `PyPI project page`_
|
||||
|
||||
using easy_install
|
||||
===================================================
|
||||
|
||||
With a working `setuptools installation`_ you can type::
|
||||
|
||||
easy_install -U py
|
||||
|
||||
to get the latest release of the py lib. The ``-U`` switch
|
||||
will trigger an upgrade if you already have an older version installed.
|
||||
On Linux systems you may need to execute the command as superuser and
|
||||
on Windows you might need to write down the full path to ``easy_install``.
|
||||
The py lib and its tools are expected to work well on Linux,
|
||||
Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6.
|
||||
|
||||
**IMPORTANT NOTE**: if you are using Windows and have
|
||||
0.8 versions of the py lib on your system, please download
|
||||
and execute http://codespeak.net/svn/py/build/winpathclean.py
|
||||
This will check that no previous files are getting in the way.
|
||||
You can find out the py lib version with::
|
||||
|
||||
import py
|
||||
print py.version
|
||||
|
||||
|
||||
.. _mercurial: http://mercurial.selenic.com/wiki/
|
||||
.. _checkout:
|
||||
.. _tarball:
|
||||
|
||||
Working from version control or a tarball
|
||||
=================================================
|
||||
|
||||
To follow development or help with fixing things
|
||||
for the next release, checkout the complete code
|
||||
and documentation source with mercurial_::
|
||||
|
||||
hg clone https://bitbucket.org/hpk42/py-trunk/
|
||||
|
||||
This currrently contains a 1.0.x branch and the
|
||||
default 'trunk' branch where mainline development
|
||||
takes place. There also is a readonly subversion
|
||||
checkout available::
|
||||
|
||||
svn co https://codespeak.net/svn/py/dist
|
||||
|
||||
You can also go to the python package index and
|
||||
download and unpack a TAR file::
|
||||
|
||||
http://pypi.python.org/pypi/py/
|
||||
|
||||
activating checkout with setuptools
|
||||
--------------------------------------------
|
||||
|
||||
With a working `setuptools installation`_ you can issue::
|
||||
|
||||
python setup.py develop
|
||||
|
||||
in order to work with the tools and the lib of your checkout.
|
||||
|
||||
.. _`no-setuptools`:
|
||||
|
||||
activating a checkout or tarball without setuptools
|
||||
-------------------------------------------------------------
|
||||
|
||||
To import the py lib the ``py`` package directory needs to
|
||||
be on the ``$PYTHONPATH``. If you exexute scripts directly
|
||||
from ``py/bin/`` or ``py\bin\win32`` they will find their
|
||||
containing py lib automatically.
|
||||
|
||||
It is usually a good idea to add the parent directory of the ``py`` package
|
||||
directory to your ``PYTHONPATH`` and ``py/bin`` or ``py\bin\win32`` to your
|
||||
system wide ``PATH`` settings. There are helper scripts that set ``PYTHONPATH`` and ``PATH`` on your system:
|
||||
|
||||
on windows execute::
|
||||
|
||||
# inside autoexec.bat or shell startup
|
||||
c:\\path\to\checkout\py\env.cmd
|
||||
|
||||
on linux/OSX add this to your shell initialization::
|
||||
|
||||
# inside .bashrc
|
||||
eval `python ~/path/to/checkout/py/env.py`
|
||||
|
||||
both of which which will get you good settings
|
||||
for ``PYTHONPATH`` and ``PATH``.
|
||||
|
||||
|
||||
note: scripts look for "nearby" py-lib
|
||||
-----------------------------------------------------
|
||||
|
||||
Note that the `command line scripts`_ will look
|
||||
for "nearby" py libs, so if you have a layout like this::
|
||||
|
||||
mypkg/
|
||||
subpkg1/
|
||||
tests/
|
||||
tests/
|
||||
py/
|
||||
|
||||
issuing ``py.test subpkg1`` will use the py lib
|
||||
from that projects root directory.
|
||||
|
||||
.. _`command line scripts`: bin.html
|
||||
|
||||
Debian and RPM packages
|
||||
===================================
|
||||
|
||||
As of August 2009 pytest/pylib 1.0 RPMs and Debian packages
|
||||
are not available. You will only find 0.9 versions -
|
||||
on Debian systems look for ``python-codespeak-lib``
|
||||
and Dwayne Bailey has put together a Fedora `RPM`_.
|
||||
|
||||
If you can help with providing/upgrading distribution
|
||||
packages please use of the contact_ channels in case
|
||||
of questions or need for changes.
|
||||
|
||||
.. _contact: contact.html
|
||||
|
||||
.. _`RPM`: http://translate.sourceforge.net/releases/testing/fedora/pylib-0.9.2-1.fc9.noarch.rpm
|
||||
|
||||
.. _`setuptools installation`: http://pypi.python.org/pypi/setuptools
|
||||
|
||||
@@ -96,7 +96,7 @@ class TestFailing(object):
|
||||
|
||||
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
|
||||
l = [1,2,3]
|
||||
print "l is", l
|
||||
print ("l is %r" % l)
|
||||
a,b = l.pop()
|
||||
|
||||
def test_some_error(self):
|
||||
@@ -113,7 +113,7 @@ def test_dynamic_compile_shows_nicely():
|
||||
name = 'abc-123'
|
||||
module = py.std.imp.new_module(name)
|
||||
code = py.code.compile(src, name, 'exec')
|
||||
exec code in module.__dict__
|
||||
py.builtin.exec_(code, module.__dict__)
|
||||
py.std.sys.modules[name] = module
|
||||
module.foo()
|
||||
|
||||
10
doc/example/assertion/global_testmodule_config/conftest.py
Normal file
10
doc/example/assertion/global_testmodule_config/conftest.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import py
|
||||
mydir = py.path.local(__file__).dirpath()
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
if isinstance(item, py.test.collect.Function):
|
||||
if not item.fspath.relto(mydir):
|
||||
return
|
||||
mod = item.getparent(py.test.collect.Module).obj
|
||||
if hasattr(mod, 'hello'):
|
||||
py.builtin.print_("mod.hello", mod.hello)
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
hello = "world"
|
||||
|
||||
def test_func():
|
||||
pass
|
||||
15
doc/example/assertion/test_failures.py
Normal file
15
doc/example/assertion/test_failures.py
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
import py
|
||||
failure_demo = py.path.local(__file__).dirpath('failure_demo.py')
|
||||
|
||||
pytest_plugins = "pytest_pytester"
|
||||
|
||||
def test_failure_demo_fails_properly(testdir):
|
||||
target = testdir.tmpdir.join(failure_demo.basename)
|
||||
failure_demo.copy(target)
|
||||
failure_demo.copy(testdir.tmpdir.join(failure_demo.basename))
|
||||
result = testdir.runpytest(target)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*20 failed*"
|
||||
])
|
||||
assert result.ret != 0
|
||||
@@ -9,7 +9,7 @@ class TestStateFullThing:
|
||||
cls.classcount -= 1
|
||||
|
||||
def setup_method(self, method):
|
||||
self.id = eval(method.func_name[5:])
|
||||
self.id = eval(method.__name__[5:])
|
||||
|
||||
def test_42(self):
|
||||
assert self.classcount == 1
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
from myapp import MyApp
|
||||
from mysetup.myapp import MyApp
|
||||
|
||||
def pytest_funcarg__mysetup(request):
|
||||
return MySetup()
|
||||
@@ -1,5 +1,5 @@
|
||||
import py
|
||||
from myapp import MyApp
|
||||
from mysetup2.myapp import MyApp
|
||||
|
||||
def pytest_funcarg__mysetup(request):
|
||||
return MySetup(request)
|
||||
@@ -20,5 +20,5 @@ class MySetup:
|
||||
host = self.config.option.ssh
|
||||
if host is None:
|
||||
py.test.skip("specify ssh host with --ssh")
|
||||
return py.execnet.SshGateway(host)
|
||||
return execnet.SshGateway(host)
|
||||
|
||||
65
doc/example/funcarg/test_multi_python.py
Normal file
65
doc/example/funcarg/test_multi_python.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
|
||||
module containing a parametrized tests testing cross-python
|
||||
serialization via the pickle module.
|
||||
"""
|
||||
import py
|
||||
|
||||
pythonlist = ['python2.3', 'python2.4', 'python2.5', 'python2.6']
|
||||
# 'jython' 'python3.1']
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'python1' in metafunc.funcargnames:
|
||||
assert 'python2' in metafunc.funcargnames
|
||||
for obj in metafunc.function.multiarg.kwargs['obj']:
|
||||
for py1 in pythonlist:
|
||||
for py2 in pythonlist:
|
||||
metafunc.addcall(id="%s-%s-%s" % (py1, py2, obj),
|
||||
param=(py1, py2, obj))
|
||||
|
||||
@py.test.mark.multiarg(obj=[42, {}, {1:3},])
|
||||
def test_basic_objects(python1, python2, obj):
|
||||
python1.dumps(obj)
|
||||
python2.load_and_is_true("obj == %s" % obj)
|
||||
|
||||
def pytest_funcarg__python1(request):
|
||||
tmpdir = request.getfuncargvalue("tmpdir")
|
||||
picklefile = tmpdir.join("data.pickle")
|
||||
return Python(request.param[0], picklefile)
|
||||
|
||||
def pytest_funcarg__python2(request):
|
||||
python1 = request.getfuncargvalue("python1")
|
||||
return Python(request.param[1], python1.picklefile)
|
||||
|
||||
def pytest_funcarg__obj(request):
|
||||
return request.param[2]
|
||||
|
||||
class Python:
|
||||
def __init__(self, version, picklefile):
|
||||
self.pythonpath = py.path.local.sysfind(version)
|
||||
if not self.pythonpath:
|
||||
py.test.skip("%r not found" %(version,))
|
||||
self.picklefile = picklefile
|
||||
def dumps(self, obj):
|
||||
dumpfile = self.picklefile.dirpath("dump.py")
|
||||
dumpfile.write(py.code.Source("""
|
||||
import pickle
|
||||
f = open(%r, 'wb')
|
||||
s = pickle.dump(%r, f)
|
||||
f.close()
|
||||
""" % (str(self.picklefile), obj)))
|
||||
py.process.cmdexec("%s %s" %(self.pythonpath, dumpfile))
|
||||
|
||||
def load_and_is_true(self, expression):
|
||||
loadfile = self.picklefile.dirpath("load.py")
|
||||
loadfile.write(py.code.Source("""
|
||||
import pickle
|
||||
f = open(%r, 'rb')
|
||||
obj = pickle.load(f)
|
||||
f.close()
|
||||
res = eval(%r)
|
||||
if not res:
|
||||
raise SystemExit(1)
|
||||
""" % (str(self.picklefile), expression)))
|
||||
print (loadfile)
|
||||
py.process.cmdexec("%s %s" %(self.pythonpath, loadfile))
|
||||
15
doc/example/funcarg/urloption/conftest.py
Normal file
15
doc/example/funcarg/urloption/conftest.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# conftest.py
|
||||
import py
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
grp = parser.getgroup("testserver options")
|
||||
grp.addoption("--url", action="store", default=None,
|
||||
help="url for testserver")
|
||||
|
||||
def pytest_funcarg__url(request):
|
||||
url = request.config.getvalue("url")
|
||||
if url is None:
|
||||
py.test.skip("need --url")
|
||||
return url
|
||||
|
||||
16
doc/example/xfail_demo.py
Normal file
16
doc/example/xfail_demo.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import py
|
||||
|
||||
@py.test.mark.xfail
|
||||
def test_hello():
|
||||
assert 0
|
||||
|
||||
@py.test.mark.xfail(run=False)
|
||||
def test_hello2():
|
||||
assert 0
|
||||
|
||||
@py.test.mark.xfail("hasattr(os, 'sep')")
|
||||
def test_hello3():
|
||||
assert 0
|
||||
|
||||
def test_hello5():
|
||||
py.test.xfail("reason")
|
||||
264
doc/execnet.txt
264
doc/execnet.txt
@@ -2,263 +2,11 @@
|
||||
py.execnet: *elastic* distributed programming
|
||||
==============================================================================
|
||||
|
||||
``execnet`` helps you to:
|
||||
Since pylib 1.1 "py.execnet" ceased to exist and is now available
|
||||
as a separately developed `execnet standalone package`_.
|
||||
|
||||
* ad-hoc instantiate local or remote Python Processes
|
||||
* send code for execution in one or many processes
|
||||
* send and receive data between processes through channels
|
||||
|
||||
One of it's unique features is that it uses a **zero-install**
|
||||
technique: no manual installation steps are required on
|
||||
remote places, only a basic working Python interpreter
|
||||
and some input/output connection to it.
|
||||
|
||||
There is a `EuroPython2009 talk`_ from July 2009 with
|
||||
examples and some pictures.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
.. _`EuroPython2009 talk`: http://codespeak.net/download/py/ep2009-execnet.pdf
|
||||
|
||||
Gateways: immediately spawn local or remote process
|
||||
===================================================
|
||||
|
||||
In order to send code to a remote place or a subprocess
|
||||
you need to instantiate a so-called Gateway object.
|
||||
There are currently three Gateway classes:
|
||||
|
||||
* :api:`py.execnet.PopenGateway` to open a subprocess
|
||||
on the local machine. Useful for making use
|
||||
of multiple processors to to contain code execution
|
||||
in a separated environment.
|
||||
|
||||
* :api:`py.execnet.SshGateway` to connect to
|
||||
a remote ssh server and distribute execution to it.
|
||||
|
||||
* :api:`py.execnet.SocketGateway` a way to connect to
|
||||
a remote Socket based server. *Note* that this method
|
||||
requires a manually started
|
||||
:source:py/execnet/script/socketserver.py
|
||||
script. You can run this "server script" without
|
||||
having the py lib installed on the remote system
|
||||
and you can setup it up as permanent service.
|
||||
|
||||
|
||||
remote_exec: execute source code remotely
|
||||
===================================================
|
||||
|
||||
All gateways offer remote code execution via this high level function::
|
||||
|
||||
def remote_exec(source):
|
||||
"""return channel object for communicating with the asynchronously
|
||||
executing 'source' code which will have a corresponding 'channel'
|
||||
object in its executing namespace."""
|
||||
|
||||
With `remote_exec` you send source code to the other
|
||||
side and get both a local and a remote Channel_ object,
|
||||
which you can use to have the local and remote site
|
||||
communicate data in a structured way. Here is
|
||||
an example for reading the PID::
|
||||
|
||||
>>> import py
|
||||
>>> gw = py.execnet.PopenGateway()
|
||||
>>> channel = gw.remote_exec("""
|
||||
... import os
|
||||
... channel.send(os.getpid())
|
||||
... """)
|
||||
>>> remote_pid = channel.receive()
|
||||
>>> remote_pid != py.std.os.getpid()
|
||||
True
|
||||
|
||||
.. _`Channel`:
|
||||
.. _`channel-api`:
|
||||
.. _`exchange data`:
|
||||
|
||||
Channels: bidirectionally exchange data between hosts
|
||||
=======================================================
|
||||
|
||||
A channel object allows to send and receive data between
|
||||
two asynchronously running programs. When calling
|
||||
`remote_exec` you will get a channel object back and
|
||||
the code fragment running on the other side will
|
||||
see a channel object in its global namespace.
|
||||
|
||||
Here is the interface of channel objects::
|
||||
|
||||
#
|
||||
# API for sending and receiving anonymous values
|
||||
#
|
||||
channel.send(item):
|
||||
sends the given item to the other side of the channel,
|
||||
possibly blocking if the sender queue is full.
|
||||
Note that items need to be marshallable (all basic
|
||||
python types are).
|
||||
|
||||
channel.receive():
|
||||
receives an item that was sent from the other side,
|
||||
possibly blocking if there is none.
|
||||
Note that exceptions from the other side will be
|
||||
reraised as gateway.RemoteError exceptions containing
|
||||
a textual representation of the remote traceback.
|
||||
|
||||
channel.waitclose(timeout=None):
|
||||
wait until this channel is closed. Note that a closed
|
||||
channel may still hold items that will be received or
|
||||
send. Note that exceptions from the other side will be
|
||||
reraised as gateway.RemoteError exceptions containing
|
||||
a textual representation of the remote traceback.
|
||||
|
||||
channel.close():
|
||||
close this channel on both the local and the remote side.
|
||||
A remote side blocking on receive() on this channel
|
||||
will get woken up and see an EOFError exception.
|
||||
|
||||
|
||||
.. _xspec:
|
||||
|
||||
|
||||
XSpec: string specification for gateway type and configuration
|
||||
===============================================================
|
||||
|
||||
``py.execnet`` supports a simple extensible format for
|
||||
specifying and configuring Gateways for remote execution.
|
||||
You can use a string specification to instantiate a new gateway,
|
||||
for example a new SshGateway::
|
||||
|
||||
gateway = py.execnet.makegateway("ssh=myhost")
|
||||
|
||||
Let's look at some examples for valid specifications.
|
||||
Specification for an ssh connection to `wyvern`, running on python2.4 in the (newly created) 'mycache' subdirectory::
|
||||
|
||||
ssh=wyvern//python=python2.4//chdir=mycache
|
||||
|
||||
Specification of a python2.5 subprocess; with a low CPU priority ("nice" level). Current dir will be the current dir of the instantiator (that's true for all 'popen' specifications unless they specify 'chdir')::
|
||||
|
||||
popen//python=2.5//nice=20
|
||||
|
||||
Specification of a Python Socket server process that listens on 192.168.1.4:8888; current dir will be the 'pyexecnet-cache' sub directory which is used a default for all remote processes::
|
||||
|
||||
socket=192.168.1.4:8888
|
||||
|
||||
More generally, a specification string has this general format::
|
||||
|
||||
key1=value1//key2=value2//key3=value3
|
||||
|
||||
If you omit a value, a boolean true value is assumed. Currently
|
||||
the following key/values are supported:
|
||||
|
||||
* ``popen`` for a PopenGateway
|
||||
* ``ssh=host`` for a SshGateway
|
||||
* ``socket=address:port`` for a SocketGateway
|
||||
* ``python=executable`` for specifying Python executables
|
||||
* ``chdir=path`` change remote working dir to given relative or absolute path
|
||||
* ``nice=value`` decrease remote nice level if platforms supports it
|
||||
|
||||
|
||||
Examples of py.execnet usage
|
||||
===============================================================
|
||||
|
||||
Compare cwd() of Popen Gateways
|
||||
----------------------------------------
|
||||
|
||||
A PopenGateway has the same working directory as the instantiatior::
|
||||
|
||||
>>> import py, os
|
||||
>>> gw = py.execnet.PopenGateway()
|
||||
>>> ch = gw.remote_exec("import os; channel.send(os.getcwd())")
|
||||
>>> res = ch.receive()
|
||||
>>> assert res == os.getcwd()
|
||||
>>> gw.exit()
|
||||
|
||||
Synchronously receive results from two sub processes
|
||||
-----------------------------------------------------
|
||||
|
||||
Use MultiChannels for receiving multiple results from remote code::
|
||||
|
||||
>>> import py
|
||||
>>> ch1 = py.execnet.PopenGateway().remote_exec("channel.send(1)")
|
||||
>>> ch2 = py.execnet.PopenGateway().remote_exec("channel.send(2)")
|
||||
>>> mch = py.execnet.MultiChannel([ch1, ch2])
|
||||
>>> l = mch.receive_each()
|
||||
>>> assert len(l) == 2
|
||||
>>> assert 1 in l
|
||||
>>> assert 2 in l
|
||||
|
||||
Asynchronously receive results from two sub processes
|
||||
-----------------------------------------------------
|
||||
|
||||
Use ``MultiChannel.make_receive_queue()`` for asynchronously receiving
|
||||
multiple results from remote code. This standard Queue provides
|
||||
``(channel, result)`` tuples which allows to determine where
|
||||
a result comes from::
|
||||
|
||||
>>> import py
|
||||
>>> ch1 = py.execnet.PopenGateway().remote_exec("channel.send(1)")
|
||||
>>> ch2 = py.execnet.PopenGateway().remote_exec("channel.send(2)")
|
||||
>>> mch = py.execnet.MultiChannel([ch1, ch2])
|
||||
>>> queue = mch.make_receive_queue()
|
||||
>>> chan1, res1 = queue.get() # you may also specify a timeout
|
||||
>>> chan2, res2 = queue.get()
|
||||
>>> res1 + res2
|
||||
3
|
||||
>>> assert chan1 in (ch1, ch2)
|
||||
>>> assert chan2 in (ch1, ch2)
|
||||
>>> assert chan1 != chan2
|
||||
|
||||
Receive file contents from remote SSH account
|
||||
-----------------------------------------------------
|
||||
|
||||
Here is a small program that you can use to retrieve
|
||||
contents of remote files::
|
||||
|
||||
import py
|
||||
# open a gateway to a fresh child process
|
||||
gw = py.execnet.SshGateway('codespeak.net')
|
||||
channel = gw.remote_exec("""
|
||||
for fn in channel:
|
||||
f = open(fn, 'rb')
|
||||
channel.send(f.read())
|
||||
f.close()
|
||||
""")
|
||||
|
||||
for fn in somefilelist:
|
||||
channel.send(fn)
|
||||
content = channel.receive()
|
||||
# process content
|
||||
|
||||
# later you can exit / close down the gateway
|
||||
gw.exit()
|
||||
|
||||
|
||||
Instantiate a socket server in a new subprocess
|
||||
-----------------------------------------------------
|
||||
|
||||
The following example opens a PopenGateway, i.e. a python
|
||||
child process, and starts a socket server within that process
|
||||
and then opens a second gateway to the freshly started
|
||||
socketserver::
|
||||
|
||||
import py
|
||||
|
||||
popengw = py.execnet.PopenGateway()
|
||||
socketgw = py.execnet.SocketGateway.new_remote(popengw, ("127.0.0.1", 0))
|
||||
|
||||
print socketgw._rinfo() # print some info about the remote environment
|
||||
|
||||
|
||||
Sending a module / checking if run through remote_exec
|
||||
--------------------------------------------------------------
|
||||
|
||||
You can pass a module object to ``remote_exec`` in which case
|
||||
its source code will be sent. No dependencies will be transferred
|
||||
so the module must be self-contained or only use modules that are
|
||||
installed on the "other" side. Module code can detect if it is
|
||||
running in a remote_exec situation by checking for the special
|
||||
``__name__`` attribute like this::
|
||||
|
||||
if __name__ == '__channelexec__':
|
||||
# ... call module functions ...
|
||||
|
||||
If you have previosly used "py.execnet.*" and the 1.0 API just
|
||||
rename all occurences of the string "``py.execnet.``" with the
|
||||
string "``execnet.``" as execnet-1.0 is API compatible.
|
||||
|
||||
.. _`execnet standalone package`: http://codespeak.net/execnet
|
||||
|
||||
155
doc/faq.txt
155
doc/faq.txt
@@ -6,81 +6,97 @@ Frequently Asked Questions
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
On naming, nose and magic
|
||||
============================
|
||||
|
||||
Why the ``py`` naming? what is it?
|
||||
------------------------------------
|
||||
On naming, nosetests, licensing and magic
|
||||
===========================================
|
||||
|
||||
Because the name was kind of available and there was the
|
||||
idea to have the package evolve into a "standard" library
|
||||
kind of thing that works cross-python versions and is
|
||||
not tied to a particular CPython revision or its release
|
||||
cycle. Clearly, this was ambitious and the naming
|
||||
has maybe haunted the project rather than helping it.
|
||||
There may be a project name change and possibly a
|
||||
split up into different projects sometime.
|
||||
Why the ``py`` naming? Why not ``pytest``?
|
||||
----------------------------------------------------
|
||||
|
||||
This mostly has historic reasons - the aim is
|
||||
to get away from the somewhat questionable 'py' name
|
||||
at some point. These days (2010) the 'py' library
|
||||
almost completely comprises APIs that are used
|
||||
by the ``py.test`` tool. There also are some
|
||||
other uses, e.g. of the ``py.path.local()`` and
|
||||
other path implementations. So it requires some
|
||||
work to factor them out and do the shift.
|
||||
|
||||
Why the ``py.test`` naming?
|
||||
------------------------------------
|
||||
|
||||
the py lib contains other command line tools that
|
||||
all share the ``py.`` prefix which makes it easy
|
||||
to use TAB-completion on the shell. Another motivation
|
||||
was to make it obvious where testing functionality
|
||||
for the ``py.test`` command line tool is: in the
|
||||
``py.test`` package name space.
|
||||
because of TAB-completion under Bash/Shells. If you hit
|
||||
``py.<TAB>`` you'll get a list of available development
|
||||
tools that all share the ``py.`` prefix. Another motivation
|
||||
was to unify the package ("py.test") and tool filename.
|
||||
|
||||
What's the relation to ``nosetests``?
|
||||
----------------------------------------
|
||||
What's py.test's relation to ``nosetests``?
|
||||
---------------------------------------------
|
||||
|
||||
py.test and nose_ share basic philosophy when it comes
|
||||
to running Python tests. In fact,
|
||||
with py.test-1.0.1 it is easy to run many test suites
|
||||
with py.test-1.1.0 it is ever easier to run many test suites
|
||||
that currently work with ``nosetests``. nose_ was created
|
||||
as a clone of ``py.test`` when it was in the ``0.8`` release
|
||||
as a clone of ``py.test`` when py.test was in the ``0.8`` release
|
||||
cycle so some of the newer features_ introduced with py.test-1.0
|
||||
have no counterpart in nose_.
|
||||
and py.test-1.1 have no counterpart in nose_.
|
||||
|
||||
.. _nose: http://somethingaboutorange.com/mrl/projects/nose/0.11.1/
|
||||
.. _features: test/features.html
|
||||
.. _apipkg: http://pypi.python.org/pypi/apipkg
|
||||
|
||||
What's all this "magic" with py.test?
|
||||
|
||||
What's this "magic" with py.test?
|
||||
----------------------------------------
|
||||
|
||||
"All this magic" usually boils down to two issues:
|
||||
issues where people have used the term "magic" in the past:
|
||||
|
||||
* There is a special tweak to importing: `py/__init__.py`_ contains
|
||||
a dictionary which maps the importable ``py.*`` namespaces to
|
||||
objects in files. When looking at the project source code
|
||||
you see imports like ``from py.__.test.session import Session``. The
|
||||
the double ``__`` underscore indicates the "normal" python
|
||||
filesystem/namespace coupled import, i.e. it points to
|
||||
``py/test/session.py``'s ``Session`` object. However,
|
||||
from the outside you use the "non-underscore" `py namespaces`_
|
||||
so this distinction usually only shows up if you hack
|
||||
on internal code or see internal tracebacks.
|
||||
* `py/__init__.py`_ uses the apipkg_ mechanism for lazy-importing
|
||||
and full control on what API you get when importing "import py".
|
||||
|
||||
* when an ``assert`` fails, py.test re-interprets the expression
|
||||
to show intermediate values. This allows to use the plain ``assert``
|
||||
statement instead of the many methods that you otherwise need
|
||||
to mimick this behaviour. This means that in case of a failing
|
||||
assert, your expressions gets evaluated *twice*. If your expression
|
||||
has side effects the outcome may be different. If the test suddenly
|
||||
passes you will get a detailed message. It is good practise, anyway,
|
||||
to not have asserts with side effects. ``py.test --nomagic`` turns
|
||||
off assert re-intepretation.
|
||||
* when an ``assert`` statement fails, py.test re-interprets the expression
|
||||
to show intermediate values if a test fails. If your expression
|
||||
has side effects the intermediate values may not be the same, obfuscating
|
||||
the initial error (this is also explained at the command line if it happens).
|
||||
``py.test --no-assert`` turns off assert re-intepretation.
|
||||
Sidenote: it is good practise to avoid asserts with side effects.
|
||||
|
||||
Other than that, ``py.test`` has bugs or quirks like any other computer
|
||||
software. In fact, it has a *strong* focus on running robustly and has
|
||||
over a thousand automated tests for its own code base.
|
||||
|
||||
.. _`py namespaces`: index.html
|
||||
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/1.0.x/py/__init__.py
|
||||
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
|
||||
|
||||
Where does my ``py.test`` come/import from?
|
||||
----------------------------------------------
|
||||
|
||||
You can issue::
|
||||
|
||||
py.test --version
|
||||
|
||||
which tells you both version and import location of the tool.
|
||||
|
||||
|
||||
function arguments and parametrized tests
|
||||
===============================================
|
||||
function arguments, parametrized tests and setup
|
||||
====================================================
|
||||
|
||||
.. _funcargs: test/funcargs.html
|
||||
|
||||
Is using funcarg- versus xUnit-based setup a style question?
|
||||
---------------------------------------------------------------
|
||||
|
||||
It depends. For simple applications or for people experienced
|
||||
with nose_ or unittest-style test setup using `xUnit style setup`_
|
||||
make some sense. For larger test suites, parametrized testing
|
||||
or setup of complex test resources using funcargs_ is recommended.
|
||||
Moreover, funcargs are ideal for writing advanced test support
|
||||
code (like e.g. the monkeypatch_, the tmpdir_ or capture_ funcargs)
|
||||
because the support code can register setup/teardown functions
|
||||
in a managed class/module/function scope.
|
||||
|
||||
.. _monkeypatch: test/plugin/monkeypatch.html
|
||||
.. _tmpdir: test/plugin/tmpdir.html
|
||||
.. _capture: test/plugin/capture.html
|
||||
.. _`xUnit style setup`: test/xunit_setup.html
|
||||
.. _`pytest_nose`: test/plugin/nose.html
|
||||
|
||||
.. _`why pytest_pyfuncarg__ methods?`:
|
||||
|
||||
@@ -93,15 +109,15 @@ flexibility we decided to go for `Convention over Configuration`_ and
|
||||
allow to directly specify the factory. Besides removing the need
|
||||
for an indirection it allows to "grep" for ``pytest_funcarg__MYARG``
|
||||
and will safely find all factory functions for the ``MYARG`` function
|
||||
argument. It helps to alleviates the de-coupling of function
|
||||
argument. It helps to alleviate the de-coupling of function
|
||||
argument usage and creation.
|
||||
|
||||
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
|
||||
|
||||
Can i yield multiple values from a factory function?
|
||||
Can I yield multiple values from a factory function?
|
||||
-----------------------------------------------------
|
||||
|
||||
There are two reasons why yielding from a factory function
|
||||
There are two conceptual reasons why yielding from a factory function
|
||||
is not possible:
|
||||
|
||||
* Calling factories for obtaining test function arguments
|
||||
@@ -121,3 +137,36 @@ and implement the `parametrization scheme of your choice`_.
|
||||
.. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
|
||||
|
||||
|
||||
py.test interaction with other packages
|
||||
===============================================
|
||||
|
||||
Issues with py.test, multiprocess and setuptools?
|
||||
------------------------------------------------------------
|
||||
|
||||
On windows the multiprocess package will instantiate sub processes
|
||||
by pickling and thus implicitely re-import a lot of local modules.
|
||||
Unfortuantely, setuptools-0.6.11 does not ``if __name__=='__main__'``
|
||||
protect its generated command line script. This leads to infinite
|
||||
recursion when running a test that instantiates Processes.
|
||||
There are these workarounds:
|
||||
|
||||
* `install Distribute`_ as a drop-in replacement for setuptools
|
||||
and install py.test
|
||||
|
||||
* `directly use a checkout`_ which avoids all setuptools/Distribute
|
||||
installation
|
||||
|
||||
If those options are not available to you, you may also manually
|
||||
fix the script that is created by setuptools by inserting an
|
||||
``if __name__ == '__main__'``. Or you can create a "pytest.py"
|
||||
script with this content and invoke that with the python version::
|
||||
|
||||
import py
|
||||
if __name__ == '__main__':
|
||||
py.cmdline.pytest()
|
||||
|
||||
.. _`directly use a checkout`: install.html#directly-use-a-checkout
|
||||
|
||||
.. _`install distribute`: http://pypi.python.org/pypi/distribute#installation-instructions
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
py lib: testing and distributed programming library
|
||||
py lib: testing and filesystem abstraction
|
||||
====================================================
|
||||
|
||||
The ``py`` lib has several namespaces which help with testing,
|
||||
generating and distributing code across machines. Here is
|
||||
documentation on the most interesting ones:
|
||||
The ``py`` lib has several namespaces which help with automated testing,
|
||||
and with accessing filesystems. Here is some documentation on the
|
||||
core namespaces:
|
||||
|
||||
`py.test`_ write and deploy unit- and functional tests to multiple machines.
|
||||
|
||||
`py.execnet`_ elastic distributed programming.
|
||||
|
||||
`py.code`_: generate code and use advanced introspection/traceback support.
|
||||
|
||||
`py.path`_: use path objects to transparently access local and svn filesystems.
|
||||
@@ -31,9 +29,7 @@ Other (minor) support functionality
|
||||
|
||||
For the latest Release, see `PyPI project page`_
|
||||
|
||||
.. _`download and installation`: download.html
|
||||
.. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev
|
||||
.. _`py.execnet`: execnet.html
|
||||
.. _`py.log`: log.html
|
||||
.. _`py.io`: io.html
|
||||
.. _`py.path`: path.html
|
||||
|
||||
190
doc/install.txt
Normal file
190
doc/install.txt
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
.. _`index page`: http://pypi.python.org/pypi/py/
|
||||
|
||||
py.test/pylib installation info in a nutshell
|
||||
===================================================
|
||||
|
||||
**Pythons**: 2.4, 2.5, 2.6, 2.7, 3.0, 3.1.x, Jython-2.5.1, PyPy-1.2
|
||||
|
||||
**Operating systems**: Linux, Windows, OSX, Unix
|
||||
|
||||
**Requirements**: setuptools_ or Distribute_
|
||||
|
||||
**Installers**: easy_install_ and pip_ or `standalone`_ (new for 1.2)
|
||||
|
||||
**Distribution names**:
|
||||
|
||||
* PyPI name: ``py`` (see `index page`_ for versions)
|
||||
* redhat fedora: ``python-py``
|
||||
* debian: ``python-codespeak-lib``
|
||||
* gentoo: ``pylib``
|
||||
|
||||
**Installed scripts**: see `bin`_ for which and how scripts are installed.
|
||||
|
||||
**hg repository**: https://bitbucket.org/hpk42/py-trunk
|
||||
|
||||
.. _`bin`: bin.html
|
||||
|
||||
|
||||
Best practise: install tool and dependencies virtually
|
||||
===========================================================
|
||||
|
||||
It is recommended to work with virtual environments
|
||||
(e.g. virtualenv_ or buildout_ based) and use easy_install_
|
||||
(or pip_) for installing py.test/pylib and any dependencies
|
||||
you need to run your tests. Local virtual Python environments
|
||||
(as opposed to system-wide "global" environments) make for a more
|
||||
reproducible and reliable test environment.
|
||||
|
||||
|
||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||
.. _`buildout`: http://www.buildout.org/
|
||||
.. _pip: http://pypi.python.org/pypi/pip
|
||||
.. _`easy_install`:
|
||||
|
||||
using easy_install (from setuptools or Distribute)
|
||||
===================================================
|
||||
|
||||
Both `Distribute`_ and setuptools_ provide the ``easy_install``
|
||||
installation tool. While setuptools should work ok with
|
||||
Python2 interpreters, `Distribute`_ also works with Python3
|
||||
and it avoids some issues on Windows. In both cases you
|
||||
can open a command line window and then type::
|
||||
|
||||
easy_install -U py
|
||||
|
||||
to install the latest release of the py lib and py.test. The ``-U`` switch
|
||||
will trigger an upgrade if you already have an older version installed.
|
||||
|
||||
If you now type::
|
||||
|
||||
py.test --version
|
||||
|
||||
you should see the version number and the import location of the tool.
|
||||
Maybe you want to head on with the `quickstart`_ now?
|
||||
|
||||
.. _quickstart: test/quickstart.html
|
||||
|
||||
.. _standalone:
|
||||
|
||||
Generating a py.test standalone Script
|
||||
============================================
|
||||
|
||||
If you are a maintainer or application developer and want users
|
||||
to run tests you can use a facility to generate a standalone
|
||||
"py.test" script that you can tell users to run::
|
||||
|
||||
py.test --genscript=mytest
|
||||
|
||||
will generate a ``mytest`` script that is, in fact, a ``py.test`` under
|
||||
disguise. You can tell people to download and then e.g. run it like this::
|
||||
|
||||
python mytest --pastebin=all
|
||||
|
||||
and ask them to send you the resulting URL. The resulting script has
|
||||
all core features and runs unchanged under Python2 and Python3 interpreters.
|
||||
|
||||
Troubleshooting / known issues
|
||||
===============================
|
||||
|
||||
.. _`Jython does not create command line launchers`: http://bugs.jython.org/issue1491
|
||||
|
||||
**Jython2.5.1 on XP**: `Jython does not create command line launchers`_
|
||||
so ``py.test`` will not work correctly. You may install py.test on
|
||||
CPython and type ``py.test --genscript=mytest`` and then use
|
||||
``jython mytest`` to run py.test for your tests to run in Jython.
|
||||
|
||||
**On Linux**: If ``easy_install`` fails because it needs to run
|
||||
as the superuser you are trying to install things globally
|
||||
and need to put ``sudo`` in front of the command.
|
||||
|
||||
**On Windows**: If "easy_install" or "py.test" are not found
|
||||
please see here: `How do i run a Python program under Windows?`_
|
||||
|
||||
.. _`How do i run a Python program under Windows?`: http://www.python.org/doc/faq/windows/#how-do-i-run-a-python-program-under-windows
|
||||
|
||||
.. _mercurial: http://mercurial.selenic.com/wiki/
|
||||
.. _`Distribute`:
|
||||
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
|
||||
.. _`distribute installation`: http://pypi.python.org/pypi/distribute
|
||||
.. _checkout:
|
||||
.. _tarball:
|
||||
|
||||
Working from version control or a tarball
|
||||
=================================================
|
||||
|
||||
To follow development or start experiments, checkout the
|
||||
complete code and documentation source with mercurial_::
|
||||
|
||||
hg clone https://bitbucket.org/hpk42/py-trunk/
|
||||
|
||||
Development takes place on the 'trunk' branch.
|
||||
|
||||
You can also go to the python package index and
|
||||
download and unpack a TAR file::
|
||||
|
||||
http://pypi.python.org/pypi/py/
|
||||
|
||||
|
||||
activating a checkout with setuptools
|
||||
--------------------------------------------
|
||||
|
||||
With a working `Distribute`_ or setuptools_ installation you can type::
|
||||
|
||||
python setup.py develop
|
||||
|
||||
in order to work inline with the tools and the lib of your checkout.
|
||||
|
||||
.. _`no-setuptools`:
|
||||
|
||||
.. _`directly use a checkout`:
|
||||
|
||||
directly use a checkout or tarball / avoid setuptools
|
||||
-------------------------------------------------------------
|
||||
|
||||
Get a checkout_ or tarball_ and add paths to your environment variables:
|
||||
|
||||
* ``PYTHONPATH`` needs to contain the root directory (where ``py`` resides)
|
||||
* ``PATH`` needs to contain ``ROOT/bin`` or ``ROOT\bin\win32`` respectively.
|
||||
|
||||
There also are helper scripts that set the variables accordingly. On windows
|
||||
execute::
|
||||
|
||||
# inside autoexec.bat or shell startup
|
||||
c:\\path\to\checkout\bin\env.cmd
|
||||
|
||||
on linux/OSX add this to your shell initialization::
|
||||
|
||||
# inside e.g. .bashrc
|
||||
eval `python ~/path/to/checkout/bin/env.py`
|
||||
|
||||
both of which which will get you good settings. If you install
|
||||
the pylib this way you can easily ``hg pull && hg up`` or download
|
||||
a new tarball_ to follow the development tree.
|
||||
|
||||
|
||||
Note that the scripts manually added like this will look for
|
||||
py libs in the chain of parent directories of the current working dir.
|
||||
For example, if you have a layout like this::
|
||||
|
||||
mypkg/
|
||||
subpkg1/
|
||||
tests/
|
||||
tests/
|
||||
py/
|
||||
|
||||
issuing ``py.test subpkg1`` will use the py lib
|
||||
from that projects root directory. If in doubt over where
|
||||
the pylib comes from you can always do::
|
||||
|
||||
py.test --version
|
||||
|
||||
to see where py.test is imported from.
|
||||
|
||||
.. _`command line scripts`: bin.html
|
||||
.. _contact: contact.html
|
||||
|
||||
.. _`RPM`: http://translate.sourceforge.net/releases/testing/fedora/pylib-0.9.2-1.fc9.noarch.rpm
|
||||
|
||||
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
||||
|
||||
12
doc/io.txt
12
doc/io.txt
@@ -9,10 +9,10 @@ execution of a program.
|
||||
IO Capturing examples
|
||||
===============================================
|
||||
|
||||
:api:`py.io.StdCapture`
|
||||
``py.io.StdCapture``
|
||||
---------------------------
|
||||
|
||||
Basic Example:
|
||||
Basic Example::
|
||||
|
||||
>>> import py
|
||||
>>> capture = py.io.StdCapture()
|
||||
@@ -21,7 +21,7 @@ Basic Example:
|
||||
>>> out.strip() == "hello"
|
||||
True
|
||||
|
||||
For calling functions you may use a shortcut:
|
||||
For calling functions you may use a shortcut::
|
||||
|
||||
>>> import py
|
||||
>>> def f(): print "hello"
|
||||
@@ -29,14 +29,14 @@ For calling functions you may use a shortcut:
|
||||
>>> out.strip() == "hello"
|
||||
True
|
||||
|
||||
:api:`py.io.StdCaptureFD`
|
||||
``py.io.StdCaptureFD``
|
||||
---------------------------
|
||||
|
||||
If you also want to capture writes to the stdout/stderr
|
||||
filedescriptors you may invoke:
|
||||
filedescriptors you may invoke::
|
||||
|
||||
>>> import py, sys
|
||||
>>> capture = py.io.StdCaptureFD()
|
||||
>>> capture = py.io.StdCaptureFD(out=False, in_=False)
|
||||
>>> sys.stderr.write("world")
|
||||
>>> out,err = capture.reset()
|
||||
>>> err
|
||||
|
||||
45
doc/misc.txt
45
doc/misc.txt
@@ -5,7 +5,7 @@ Miscellaneous features of the py lib
|
||||
Mapping the standard python library into py
|
||||
===========================================
|
||||
|
||||
The :api:`py.std` object allows lazy access to
|
||||
The ``py.std`` object allows lazy access to
|
||||
standard library modules. For example, to get to the print-exception
|
||||
functionality of the standard library you can write::
|
||||
|
||||
@@ -21,9 +21,9 @@ Support for interaction with system utilities/binaries
|
||||
======================================================
|
||||
|
||||
Currently, the py lib offers two ways to interact with
|
||||
system executables. :api:`py.process.cmdexec()` invokes
|
||||
system executables. ``py.process.cmdexec()`` invokes
|
||||
the shell in order to execute a string. The other
|
||||
one, :api:`py.path.local`'s 'sysexec()' method lets you
|
||||
one, ``py.path.local``'s 'sysexec()' method lets you
|
||||
directly execute a binary.
|
||||
|
||||
Both approaches will raise an exception in case of a return-
|
||||
@@ -87,44 +87,7 @@ right version::
|
||||
binsvn = py.path.local.sysfind('svn', checker=mysvn)
|
||||
|
||||
|
||||
|
||||
Cross-Python Version compatibility helpers
|
||||
=============================================
|
||||
|
||||
sources:
|
||||
|
||||
* :source:`py/compat/`
|
||||
* :source:`py/builtin/`
|
||||
|
||||
The compat and builtin namespaces help to write code using newer python features on older python interpreters.
|
||||
|
||||
:api:`py.compat`
|
||||
----------------
|
||||
|
||||
:api:`py.compat` provides fixed versions (currently taken from Python 2.4.4) of
|
||||
a few selected modules to be able to use them across python versions. Currently these are:
|
||||
|
||||
* doctest
|
||||
* optparse
|
||||
* subprocess
|
||||
* textwrap
|
||||
|
||||
Note that for example ``import doctest`` and ``from py.compat import doctest`` result
|
||||
into two different module objects no matter what Python version you are using.
|
||||
So you should only use exactly one of these to avoid confusion in your program.
|
||||
|
||||
:api:`py.builtin`
|
||||
-----------------
|
||||
|
||||
:api:`py.builtin` provides builtin functions/types that were added in later Python
|
||||
versions. If the used Python version used does not provide these builtins the
|
||||
py lib provides some reimplementations. These currently are:
|
||||
|
||||
* enumerate
|
||||
* reversed
|
||||
* sorted
|
||||
* BaseException
|
||||
* set and frozenset (using either the builtin, if available, or the sets
|
||||
module)
|
||||
|
||||
:api:`py.builtin.BaseException` is just ``Exception`` before Python 2.5.
|
||||
The ``py.builtin`` namespace provides a number of helpers that help to write python code compatible across Python interpreters, mainly Python2 and Python3. Type ``help(py.builtin)`` on a Python prompt for a the selection of builtins.
|
||||
|
||||
30
doc/path.txt
30
doc/path.txt
@@ -3,17 +3,17 @@ py.path
|
||||
=======
|
||||
|
||||
The 'py' lib provides a uniform high-level api to deal with filesystems
|
||||
and filesystem-like interfaces: :api:`py.path`. It aims to offer a central
|
||||
and filesystem-like interfaces: ``py.path``. It aims to offer a central
|
||||
object to fs-like object trees (reading from and writing to files, adding
|
||||
files/directories, examining the types and structure, etc.), and out-of-the-box
|
||||
provides a number of implementations of this API.
|
||||
|
||||
Path implementations provided by :api:`py.path`
|
||||
Path implementations provided by ``py.path``
|
||||
===============================================
|
||||
|
||||
.. _`local`:
|
||||
|
||||
:api:`py.path.local`
|
||||
``py.path.local``
|
||||
--------------------
|
||||
|
||||
The first and most obvious of the implementations is a wrapper around a local
|
||||
@@ -21,8 +21,8 @@ filesystem. It's just a bit nicer in usage than the regular Python APIs, and
|
||||
of course all the functionality is bundled together rather than spread over a
|
||||
number of modules.
|
||||
|
||||
Example usage, here we use the :api:`py.test.ensuretemp()` function to create
|
||||
a :api:`py.path.local` object for us (which wraps a directory):
|
||||
Example usage, here we use the ``py.test.ensuretemp()`` function to create
|
||||
a ``py.path.local`` object for us (which wraps a directory):
|
||||
|
||||
.. sourcecode:: pycon
|
||||
|
||||
@@ -40,17 +40,17 @@ a :api:`py.path.local` object for us (which wraps a directory):
|
||||
>>> foofile.read(1)
|
||||
'b'
|
||||
|
||||
:api:`py.path.svnurl` and :api:`py.path.svnwc`
|
||||
``py.path.svnurl`` and ``py.path.svnwc``
|
||||
----------------------------------------------
|
||||
|
||||
Two other :api:`py.path` implementations that the py lib provides wrap the
|
||||
Two other ``py.path`` implementations that the py lib provides wrap the
|
||||
popular `Subversion`_ revision control system: the first (called 'svnurl')
|
||||
by interfacing with a remote server, the second by wrapping a local checkout.
|
||||
Both allow you to access relatively advanced features such as metadata and
|
||||
versioning, and both in a way more user-friendly manner than existing other
|
||||
solutions.
|
||||
|
||||
Some example usage of :api:`py.path.svnurl`:
|
||||
Some example usage of ``py.path.svnurl``:
|
||||
|
||||
.. sourcecode:: pycon
|
||||
|
||||
@@ -65,7 +65,7 @@ Some example usage of :api:`py.path.svnurl`:
|
||||
>>> time.strftime('%Y-%m-%d', time.gmtime(firstentry.date))
|
||||
'2004-10-02'
|
||||
|
||||
Example usage of :api:`py.path.svnwc`:
|
||||
Example usage of ``py.path.svnwc``:
|
||||
|
||||
.. sourcecode:: pycon
|
||||
|
||||
@@ -125,7 +125,7 @@ specific directory.
|
||||
Working with Paths
|
||||
.......................
|
||||
|
||||
This example shows the :api:`py.path` features to deal with
|
||||
This example shows the ``py.path`` features to deal with
|
||||
filesystem paths Note that the filesystem is never touched,
|
||||
all operations are performed on a string level (so the paths
|
||||
don't have to exist, either):
|
||||
@@ -154,7 +154,7 @@ don't have to exist, either):
|
||||
>>> p4.purebasename == "bar"
|
||||
True
|
||||
|
||||
This should be possible on every implementation of :api:`py.path`, so
|
||||
This should be possible on every implementation of ``py.path``, so
|
||||
regardless of whether the implementation wraps a UNIX filesystem, a Windows
|
||||
one, or a database or object tree, these functions should be available (each
|
||||
with their own notion of path seperators and dealing with conversions, etc.).
|
||||
@@ -189,7 +189,7 @@ Setting svn-properties
|
||||
.......................
|
||||
|
||||
As an example of 'uncommon' methods, we'll show how to read and write
|
||||
properties in an :api:`py.path.svnwc` instance:
|
||||
properties in an ``py.path.svnwc`` instance:
|
||||
|
||||
.. sourcecode:: pycon
|
||||
|
||||
@@ -246,7 +246,7 @@ on the `svn` command line, not on the bindings.
|
||||
It makes sense now to directly use the bindings.
|
||||
|
||||
Moreover, it would be good, also considering
|
||||
`py.execnet`_ distribution of programs, to
|
||||
`execnet`_ distribution of programs, to
|
||||
be able to manipulate Windows Paths on Linux
|
||||
and vice versa. So we'd like to consider
|
||||
refactoring the path implementations
|
||||
@@ -254,7 +254,7 @@ to provide this choice (and getting rid
|
||||
of platform-dependencies as much as possible).
|
||||
|
||||
There is some experimental small approach
|
||||
(:source:`py/path/gateway/`) aiming at having
|
||||
(``py/path/gateway/``) aiming at having
|
||||
a convenient Remote Path implementation.
|
||||
|
||||
There are various hacks out there to have
|
||||
@@ -269,5 +269,5 @@ the quite full interface without requiring
|
||||
to know about all details of the full path
|
||||
implementation.
|
||||
|
||||
.. _`py.execnet`: execnet.html
|
||||
.. _`execnet`: execnet.html
|
||||
|
||||
|
||||
@@ -744,6 +744,7 @@ td.toplist {
|
||||
|
||||
img#pyimg {
|
||||
float: left;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
div#navspace {
|
||||
|
||||
@@ -95,8 +95,8 @@ per-test run basetemp directory.
|
||||
.. _`function arguments`: funcargs.html
|
||||
.. _`extensions`:
|
||||
|
||||
Plugin basics
|
||||
=========================
|
||||
Plugin basics and project configuration
|
||||
=============================================
|
||||
|
||||
.. _`local plugin`:
|
||||
|
||||
@@ -125,6 +125,8 @@ Plugin discovery at tool startup
|
||||
|
||||
py.test loads plugin modules at tool startup in the following way:
|
||||
|
||||
* by loading all plugins registered through `setuptools entry points`_.
|
||||
|
||||
* by reading the ``PYTEST_PLUGINS`` environment variable
|
||||
and importing the comma-separated list of named plugins.
|
||||
|
||||
@@ -132,17 +134,13 @@ py.test loads plugin modules at tool startup in the following way:
|
||||
and loading the specified plugin before actual command line parsing.
|
||||
|
||||
* by loading all `conftest.py plugin`_ files as inferred by the command line
|
||||
invocation
|
||||
invocation (test files and all of its parent directories).
|
||||
Note that ``conftest.py`` files from sub directories are loaded
|
||||
during test collection and not at tool startup.
|
||||
|
||||
* by recursively loading all plugins specified by the
|
||||
``pytest_plugins`` variable in a ``conftest.py`` file
|
||||
|
||||
Note that at tool startup only ``conftest.py`` files in
|
||||
the directory of the specified test modules (or the current dir if None)
|
||||
or any of the parent directories are found. There is no try to
|
||||
pre-scan all subdirectories to find ``conftest.py`` files or test
|
||||
modules.
|
||||
|
||||
Specifying plugins in a test module or plugin
|
||||
-----------------------------------------------
|
||||
|
||||
@@ -160,8 +158,8 @@ must be lowercase.
|
||||
.. _`conftest.py plugin`:
|
||||
.. _`conftestplugin`:
|
||||
|
||||
conftest.py as anonymous per-project plugins
|
||||
--------------------------------------------------
|
||||
Writing per-project plugins (conftest.py)
|
||||
------------------------------------------------------
|
||||
|
||||
The purpose of ``conftest.py`` files is to allow `project-specific
|
||||
test configuration`_. They thus make for a good place to implement
|
||||
@@ -181,6 +179,55 @@ by defining the following hook in a ``conftest.py`` file:
|
||||
if config.getvalue("runall"):
|
||||
collect_ignore[:] = []
|
||||
|
||||
.. _`setuptools entry points`:
|
||||
|
||||
Writing setuptools-registered plugins
|
||||
------------------------------------------------------
|
||||
|
||||
.. _`Distribute`: http://pypi.python.org/pypi/distribute
|
||||
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
||||
|
||||
If you want to make your plugin publically available, you
|
||||
can use `setuptools`_ or `Distribute`_ which both allow
|
||||
to register an entry point. ``py.test`` will register
|
||||
all objects with the ``pytest11`` entry point.
|
||||
To make your plugin available you may insert the following
|
||||
lines in your setuptools/distribute-based setup-invocation:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# sample ./setup.py file
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="myproject",
|
||||
packages = ['myproject']
|
||||
|
||||
# the following makes a plugin available to py.test
|
||||
entry_points = {
|
||||
'pytest11': [
|
||||
'name_of_plugin = myproject.pluginmodule',
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
If a package is installed with this setup, py.test will load
|
||||
``myproject.pluginmodule`` under the ``name_of_plugin`` name
|
||||
and use it as a plugin.
|
||||
|
||||
Accessing another plugin by name
|
||||
--------------------------------------------
|
||||
|
||||
If a plugin wants to collaborate with code from
|
||||
another plugin it can obtain a reference through
|
||||
the plugin manager like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
||||
|
||||
If you want to look at the names of existing plugins, use
|
||||
the ``--traceconfig`` option.
|
||||
|
||||
.. _`well specified hooks`:
|
||||
.. _`implement hooks`:
|
||||
@@ -226,6 +273,8 @@ for example:
|
||||
|
||||
When the test run finishes this corresponding finalizer hook is called:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_unconfigure(config):
|
||||
...
|
||||
|
||||
@@ -234,7 +283,9 @@ adding global py.test helpers and functionality
|
||||
--------------------------------------------------------------------
|
||||
|
||||
If you want to make global helper functions or objects available
|
||||
to your test code you can implement:
|
||||
to your test code you can implement:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_namespace():
|
||||
""" return dictionary with items to be made available on py.test. namespace """
|
||||
@@ -293,7 +344,7 @@ A ``report`` object contains status and reporting information:
|
||||
report.failed = True or False
|
||||
report.skipped = True or False
|
||||
|
||||
The `pytest_terminal plugin`_ uses this hook to print information
|
||||
The `terminal plugin`_ uses this hook to print information
|
||||
about a test run.
|
||||
|
||||
The whole protocol described here is implemented via this hook:
|
||||
@@ -312,8 +363,8 @@ The call object contains information about a performed call:
|
||||
call.when = "setup", "call" or "teardown"
|
||||
call.outerr = None or tuple of strings representing captured stdout/stderr
|
||||
|
||||
.. _`pytest_pdb plugin`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_pdb.py
|
||||
.. _`pytest_terminal plugin`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_terminal.py
|
||||
.. _`pytest_pdb plugin`: plugin/pdb.html
|
||||
.. _`terminal plugin`: plugin/terminal.html
|
||||
|
||||
|
||||
generic collection hooks
|
||||
@@ -353,6 +404,26 @@ Python module. The return value is a custom `collection node`_ or None.
|
||||
.. XXX or ``False`` if you want to indicate that the given item should not be collected.
|
||||
|
||||
|
||||
Gateway initialization (distributed testing)
|
||||
----------------------------------------------------
|
||||
|
||||
(alpha) For distributed testing it can be useful to prepare the
|
||||
remote environment. For this you can implement the newgateway hook:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_gwmanage_newgateway(gateway, platinfo):
|
||||
""" called after a gateway is instantiated. """
|
||||
|
||||
The ``gateway`` object here has a ``spec`` attribute which is an ``execnet.XSpec``
|
||||
object, which has attributes that map key/values as specified from a ``--txspec``
|
||||
option. The platinfo object is a dictionary with information about the remote process:
|
||||
|
||||
* ``version``: remote python's ``sys.version_info``
|
||||
* ``platform``: remote ``sys.platform``
|
||||
* ``cwd``: remote ``os.getcwd``
|
||||
|
||||
|
||||
.. _`collection process`:
|
||||
.. _`collection node`:
|
||||
.. _`test collection`:
|
||||
@@ -395,6 +466,15 @@ and test classes and methods. Test functions and methods
|
||||
are prefixed ``test`` by default. Test classes must
|
||||
start with a capitalized ``Test`` prefix.
|
||||
|
||||
Customizing error messages
|
||||
-------------------------------------------------
|
||||
|
||||
On test and collection nodes ``py.test`` will invoke
|
||||
the ``node.repr_failure(excinfo)`` function which
|
||||
you may override and make it return an error
|
||||
representation string of your choice. It
|
||||
will be reported as a (red) string.
|
||||
|
||||
.. _`package name`:
|
||||
|
||||
constructing the package name for test modules
|
||||
@@ -409,13 +489,3 @@ name. Given a filesystem ``fspath`` it is constructed as follows:
|
||||
* perform ``sys.path.insert(0, basedir)``.
|
||||
|
||||
* import the root package as ``root``
|
||||
|
||||
* determine the fully qualified name for ``fspath`` by either:
|
||||
|
||||
* calling ``root.__pkg__.getimportname(fspath)`` if the
|
||||
``__pkg__`` exists.` or
|
||||
|
||||
* otherwise use the relative path of the module path to
|
||||
the base dir and turn slashes into dots and strike
|
||||
the trailing ``.py``.
|
||||
|
||||
|
||||
18
doc/test/dist.html
Normal file
18
doc/test/dist.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content=" 1 ; URL=plugin/xdist.html" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("UA-7597274-3");
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
.. _`distribute tests across machines`:
|
||||
|
||||
===================
|
||||
Distributed testing
|
||||
===================
|
||||
|
||||
``py.test`` can ad-hoc distribute test runs to multiple CPUs or remote
|
||||
machines. This allows to speed up development or to use special resources
|
||||
of remote machines. Before running tests remotely, ``py.test`` efficiently
|
||||
synchronizes your program source code to the remote place. All test results
|
||||
are reported back and displayed to your local test session. You may
|
||||
specify different Python versions and interpreters.
|
||||
|
||||
Synchronisation and running of tests only requires
|
||||
a bare Python installation on the remote side. No
|
||||
special software is installed - this is realized
|
||||
by use of the **zero installation** `py.execnet`_ mechanisms.
|
||||
|
||||
Speed up test runs by sending tests to multiple CPUs
|
||||
----------------------------------------------------------
|
||||
|
||||
To send tests to multiple CPUs, type::
|
||||
|
||||
py.test -n NUM
|
||||
|
||||
Especially for longer running tests or tests requiring
|
||||
a lot of IO this can lead to considerable speed ups.
|
||||
|
||||
|
||||
Running tests in a Python subprocess
|
||||
----------------------------------------
|
||||
|
||||
To instantiate a python2.4 sub process and send tests to it, you may type::
|
||||
|
||||
py.test -d --tx popen//python=python2.4
|
||||
|
||||
This will start a subprocess which is run with the "python2.4"
|
||||
Python interpreter, found in your system binary lookup path.
|
||||
|
||||
If you prefix the --tx option value like this::
|
||||
|
||||
--tx 3*popen//python=python2.4
|
||||
|
||||
then three subprocesses would be created and tests
|
||||
will be load-balanced across these three processes.
|
||||
|
||||
|
||||
Sending tests to remote SSH accounts
|
||||
-----------------------------------------------
|
||||
|
||||
Suppose you have a package ``mypkg`` which contains some
|
||||
tests that you can successfully run locally. And you
|
||||
have a ssh-reachable machine ``myhost``. Then
|
||||
you can ad-hoc distribute your tests by typing::
|
||||
|
||||
py.test -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
|
||||
|
||||
This will synchronize your ``mypkg`` package directory
|
||||
to an remote ssh account and then locally collect tests
|
||||
and send them to remote places for execution.
|
||||
|
||||
You can specify multiple ``--rsyncdir`` directories
|
||||
to be sent to the remote side.
|
||||
|
||||
|
||||
Sending tests to remote Socket Servers
|
||||
----------------------------------------
|
||||
|
||||
Download the single-module `socketserver.py`_ Python program
|
||||
and run it like this::
|
||||
|
||||
python socketserver.py
|
||||
|
||||
It will tell you that it starts listening on the default
|
||||
port. You can now on your home machine specify this
|
||||
new socket host with something like this::
|
||||
|
||||
py.test -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
|
||||
|
||||
|
||||
.. _`atonce`:
|
||||
|
||||
Running tests on many platforms at once
|
||||
-------------------------------------------------------------
|
||||
|
||||
The basic command to run tests on multiple platforms is::
|
||||
|
||||
py.test --dist=each --tx=spec1 --tx=spec2
|
||||
|
||||
If you specify a windows host, an OSX host and a Linux
|
||||
environment this command will send each tests to all
|
||||
platforms - and report back failures from all platforms
|
||||
at once. The provided specifications strings
|
||||
use the `xspec syntax`_.
|
||||
|
||||
.. _`xspec syntax`: ../execnet.html#xspec
|
||||
|
||||
.. _`socketserver.py`: http://codespeak.net/svn/py/dist/py/execnet/script/socketserver.py
|
||||
.. _`py.execnet`: ../execnet.html
|
||||
|
||||
Specifying test exec environments in a conftest.py
|
||||
-------------------------------------------------------------
|
||||
|
||||
Instead of specifying command line options, you can
|
||||
put options values in a ``conftest.py`` file like this::
|
||||
|
||||
pytest_option_tx = ['ssh=myhost//python=python2.5', 'popen//python=python2.5']
|
||||
pytest_option_dist = True
|
||||
|
||||
Any commandline ``--tx`` specifictions will add to the list of available execution
|
||||
environments.
|
||||
|
||||
Specifying "rsync" dirs in a conftest.py
|
||||
-------------------------------------------------------------
|
||||
|
||||
In your ``mypkg/conftest.py`` you may specify directories to synchronise
|
||||
or to exclude::
|
||||
|
||||
rsyncdirs = ['.', '../plugins']
|
||||
rsyncignore = ['_cache']
|
||||
|
||||
These directory specifications are relative to the directory
|
||||
where the ``conftest.py`` is found.
|
||||
|
||||
|
||||
@@ -9,24 +9,42 @@ py.test feature overview
|
||||
mature command line testing tool
|
||||
====================================================
|
||||
|
||||
py.test is a command line tool to collect and run automated tests. It
|
||||
runs well on Linux, Windows and OSX Python 2.4 through to 2.6 versions.
|
||||
It can distribute a single test run to multiple machines. It is used in
|
||||
many projects, ranging from running 10 thousands of tests integrated
|
||||
with buildbot to a few inlined tests on a command line script.
|
||||
py.test is a command line tool to collect, run and report about automated tests. It runs well on Linux, Windows and OSX and on Python 2.4 through to 3.1 versions.
|
||||
It is used in many projects, ranging from running 10 thousands of tests
|
||||
to a few inlined tests on a command line script. As of version 1.2 you can also
|
||||
generate a no-dependency py.test-equivalent standalone script that you
|
||||
can distribute along with your application.
|
||||
|
||||
.. _`autocollect`:
|
||||
extensive easy plugin system
|
||||
======================================================
|
||||
|
||||
automatically collects and executes tests
|
||||
===============================================
|
||||
.. _`suprisingly easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
||||
|
||||
py.test discovers tests automatically by looking at
|
||||
specified directories and its files for common
|
||||
naming patterns. As ``py.test`` operates as a separate
|
||||
cmdline tool you can easily have a command line utility and
|
||||
some tests in the same file.
|
||||
py.test delegates almost all aspects of its operation to plugins_.
|
||||
It is `suprisingly easy`_ to add command line options or
|
||||
do other kind of add-ons and customizations. This can
|
||||
be done per-project or by distributing a global plugin.
|
||||
One can can thus modify or add aspects for purposes such as:
|
||||
|
||||
supports many testing practises and methods
|
||||
* reporting extensions
|
||||
* customizing collection and execution of tests
|
||||
* running and managing non-python tests
|
||||
* managing domain-specific test state setup
|
||||
* adding non-python tests into the run, e.g. driven by data files
|
||||
|
||||
.. _`plugins`: plugin/index.html
|
||||
|
||||
distributing tests to your CPUs and SSH accounts
|
||||
==========================================================
|
||||
|
||||
.. _`pytest-xdist`: plugin/xdist.html
|
||||
|
||||
Through the use of the separately released `pytest-xdist`_ plugin you
|
||||
can seemlessly distribute runs to multiple CPUs or remote computers
|
||||
through SSH and sockets. This plugin also offers a ``--looponfailing``
|
||||
mode which will continously re-run only failing tests in a subprocess.
|
||||
|
||||
supports several testing practises and methods
|
||||
==================================================================
|
||||
|
||||
py.test supports many testing methods conventionally used in
|
||||
@@ -40,9 +58,20 @@ with figleaf`_ or `Javasript unit- and functional testing`_.
|
||||
.. _`Javasript unit- and functional testing`: plugin/oejskit.html
|
||||
.. _`coverage testing with figleaf`: plugin/figleaf.html
|
||||
|
||||
integrates well with CI systems
|
||||
====================================================
|
||||
|
||||
py.test can produce JUnitXML style output as well as formatted
|
||||
"resultlog" files that can be postprocessed by Continous Integration
|
||||
systems such as Hudson or Buildbot easily. It also provides command
|
||||
line options to control test configuration lookup behaviour or ignoring
|
||||
certain tests or directories.
|
||||
|
||||
no-boilerplate test functions with Python
|
||||
===================================================
|
||||
|
||||
.. _`autocollect`:
|
||||
|
||||
automatic Python test discovery
|
||||
------------------------------------
|
||||
|
||||
@@ -53,13 +82,18 @@ filename are inspected for finding tests:
|
||||
* classes with a leading ``Test`` name and ``test`` prefixed methods.
|
||||
* ``unittest.TestCase`` subclasses
|
||||
|
||||
test functions can run with different argument sets
|
||||
-----------------------------------------------------------
|
||||
parametrizing test functions and advanced functional testing
|
||||
--------------------------------------------------------------
|
||||
|
||||
py.test offers the unique `funcargs mechanism`_ for setting up
|
||||
and passing project-specific objects to Python test functions.
|
||||
Test Parametrization happens by triggering a call to the same test
|
||||
functions with different argument values.
|
||||
function with different argument values. For doing fixtures
|
||||
using the funcarg mechanism makes your test and setup code
|
||||
more efficient and more readable. This is especially true
|
||||
for functional tests which might depend on command line
|
||||
options and a setup that needs to be shared across
|
||||
a whole test run.
|
||||
|
||||
per-test capturing of output, including subprocesses
|
||||
----------------------------------------------------
|
||||
@@ -96,14 +130,35 @@ first failed and then succeeded.
|
||||
asserting expected exceptions
|
||||
----------------------------------------
|
||||
|
||||
In order to write assertions about exceptions, you use
|
||||
one of two forms::
|
||||
In order to write assertions about exceptions, you can use
|
||||
``py.test.raises`` as a context manager like this:
|
||||
|
||||
py.test.raises(Exception, func, *args, **kwargs)
|
||||
py.test.raises(Exception, "func(*args, **kwargs)")
|
||||
.. sourcecode:: python
|
||||
|
||||
with py.test.raises(ZeroDivisionError):
|
||||
1 / 0
|
||||
|
||||
and if you need to have access to the actual exception info you may use:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
with py.test.raises(RuntimeError) as excinfo:
|
||||
def f():
|
||||
f()
|
||||
f()
|
||||
|
||||
# do checks related to excinfo.type, excinfo.value, excinfo.traceback
|
||||
|
||||
If you want to write test code that works on Python2.4 as well,
|
||||
you may also use two other ways to test for an expected exception:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
py.test.raises(ExpectedException, func, *args, **kwargs)
|
||||
py.test.raises(ExpectedException, "func(*args, **kwargs)")
|
||||
|
||||
both of which execute the specified function with args and kwargs and
|
||||
asserts that the given ``Exception`` is raised. The reporter will
|
||||
asserts that the given ``ExpectedException`` is raised. The reporter will
|
||||
provide you with helpful output in case of failures such as *no
|
||||
exception* or *wrong exception*.
|
||||
|
||||
@@ -123,61 +178,27 @@ command line. Using the `--pdb`` option you can automatically activate
|
||||
a PDB `Python debugger`_ when a test fails.
|
||||
|
||||
advanced skipping of tests
|
||||
-------------------------------
|
||||
======================================
|
||||
|
||||
If you want to skip tests you can use ``py.test.skip`` within
|
||||
test or setup functions. Example::
|
||||
|
||||
def test_hello():
|
||||
if sys.platform != "win32":
|
||||
py.test.skip("only win32 supported")
|
||||
|
||||
You can also use a helper to skip on a failing import::
|
||||
|
||||
docutils = py.test.importorskip("docutils")
|
||||
|
||||
or to skip if a library does not have the right version::
|
||||
|
||||
docutils = py.test.importorskip("docutils", minversion="0.3")
|
||||
|
||||
The version will be read from the specified module's ``__version__`` attribute.
|
||||
py.test has `advanced support for skipping tests`_ or expecting
|
||||
failures on tests on certain platforms. Apart from the
|
||||
minimal py.test style also unittest- and nose-style tests
|
||||
can make use of this feature.
|
||||
|
||||
.. _`advanced support for skipping tests`: plugin/skipping.html
|
||||
.. _`funcargs mechanism`: funcargs.html
|
||||
.. _`unittest.py`: http://docs.python.org/library/unittest.html
|
||||
.. _`doctest.py`: http://docs.python.org/library/doctest.html
|
||||
.. _`xUnit style setup`: xunit_setup.html
|
||||
.. _`pytest_nose`: plugin/nose.html
|
||||
|
||||
load-balance test runs to multiple CPUs
|
||||
========================================
|
||||
|
||||
For large test suites you can distribute your
|
||||
tests to multiple CPUs by issuing for example::
|
||||
|
||||
py.test -n 3
|
||||
|
||||
Read more on `distributed testing`_.
|
||||
|
||||
.. _`distributed testing`: dist.html
|
||||
|
||||
ad-hoc run tests cross-platform
|
||||
==================================================
|
||||
|
||||
py.test supports the sending of tests to
|
||||
remote ssh-accounts, socket servers.
|
||||
It can `ad-hoc run your test on multiple
|
||||
platforms one a single test run`. Ad-hoc
|
||||
means that there are **no installation
|
||||
requirements whatsoever** on the remote side.
|
||||
|
||||
.. _`ad-hoc run your test on multiple platforms one a single test run`: dist.html#atonce
|
||||
|
||||
advanced test selection and running modes
|
||||
=========================================================
|
||||
|
||||
.. _`selection by keyword`:
|
||||
|
||||
``py.test --looponfailing`` allows to run a test suite,
|
||||
``py.test --looponfailing`` (implemented through the external
|
||||
`pytest-xdist`_ plugin) allows to run a test suite,
|
||||
memorize all failures and then loop over the failing set
|
||||
of tests until they all pass. It will re-start running
|
||||
the tests when it detects file changes in your project.
|
||||
@@ -202,7 +223,9 @@ keyword.
|
||||
By default, all filename parts and
|
||||
class/function names of a test function are put into the set
|
||||
of keywords for a given test. You can specify additional
|
||||
kewords like this::
|
||||
kewords like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
@py.test.mark.webtest
|
||||
def test_send_http():
|
||||
@@ -211,23 +234,8 @@ kewords like this::
|
||||
and then use those keywords to select tests. See the `pytest_keyword`_
|
||||
plugin for more information.
|
||||
|
||||
.. _`pytest_keyword`: plugin/keyword.html
|
||||
.. _`pytest_keyword`: plugin/mark.html
|
||||
|
||||
easy to extend
|
||||
=========================================
|
||||
|
||||
py.test has advanced `extension mechanisms`_
|
||||
with a growing `list of default plugins`_.
|
||||
One can can easily modify or add aspects for for
|
||||
purposes such as:
|
||||
|
||||
* reporting extensions
|
||||
* customizing collection and execution of tests
|
||||
* running and managing non-python tests
|
||||
* managing domain-specific test state setup
|
||||
|
||||
.. _`list of default plugins`: plugin/index.html
|
||||
.. _`extension mechanisms`: customize.html#extensions
|
||||
|
||||
.. _`reStructured Text`: http://docutils.sourceforge.net
|
||||
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
|
||||
|
||||
@@ -1,43 +1,320 @@
|
||||
==========================================================
|
||||
**funcargs**: test function arguments FTW
|
||||
==========================================================
|
||||
==============================================================
|
||||
**funcargs**: advanced test fixtures and parametrization
|
||||
==============================================================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
Goals of the "funcarg" mechanism
|
||||
==========================================
|
||||
what is a "funcarg"?
|
||||
=================================================
|
||||
|
||||
Since version 1.0 py.test features the "funcarg" mechanism which
|
||||
allows a Python test function to take arguments independently provided
|
||||
by factory functions. Factory functions allow to encapsulate
|
||||
all setup and fixture glue code into nicely separated objects
|
||||
and provide a natural way for writing python test functions.
|
||||
Compared to `xUnit style`_ the new mechanism is meant to:
|
||||
|
||||
* make test functions easier to write and to read
|
||||
* isolate test fixture creation to a single place
|
||||
* bring new flexibility and power to test state management
|
||||
* naturally extend towards parametrizing test functions
|
||||
with multiple argument sets
|
||||
* enable creation of zero-boilerplate test helper objects that
|
||||
interact with the execution of a test function, see the
|
||||
`blog post about the monkeypatch funcarg`_.
|
||||
|
||||
If you find issues or have further suggestions for improving
|
||||
the mechanism you are welcome to checkout `contact possibilities`_ page.
|
||||
A *funcarg* is the short name for "test function argument". Each python test function invocation may receive one or multiple function arguments. Function argument values can be created next to the test code or in separate test configuration files which allows test functions to remain ignorant of how its base test values are created. A test function can also be called multiple times with different sets of function arguments, allowing for arbitrary parametrization. A Funcarg parameter can be any value, a simple number or an application object.
|
||||
|
||||
.. _`contact possibilities`: ../contact.html
|
||||
.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
|
||||
|
||||
.. _`blog post about the monkeypatch funcarg`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
|
||||
.. _`xUnit style`: xunit_setup.html
|
||||
|
||||
|
||||
.. _`funcarg factory`:
|
||||
.. _factory:
|
||||
|
||||
funcarg factories: creating test function arguments
|
||||
==============================================================
|
||||
|
||||
Test functions can specify one ore more arguments ("funcargs")
|
||||
and a test module or plugin can define factory functions that provide
|
||||
the function argument. Let's look at a simple self-contained
|
||||
example that you can put into a test module:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# ./test_simplefactory.py
|
||||
def pytest_funcarg__myfuncarg(request):
|
||||
return 42
|
||||
|
||||
def test_function(myfuncarg):
|
||||
assert myfuncarg == 17
|
||||
|
||||
If you run this with ``py.test test_simplefactory.py`` you see something like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
=========================== test session starts ============================
|
||||
python: platform linux2 -- Python 2.6.2
|
||||
test object 1: /home/hpk/hg/py/trunk/example/funcarg/test_simplefactory.py
|
||||
|
||||
test_simplefactory.py F
|
||||
|
||||
================================ FAILURES ==================================
|
||||
______________________________ test_function _______________________________
|
||||
|
||||
myfuncarg = 42
|
||||
|
||||
def test_function(myfuncarg):
|
||||
> assert myfuncarg == 17
|
||||
E assert 42 == 17
|
||||
|
||||
test_simplefactory.py:6: AssertionError
|
||||
======================== 1 failed in 0.11 seconds ==========================
|
||||
|
||||
|
||||
This means that the test function was called with a ``myfuncarg`` value
|
||||
of ``42`` and the assert fails accordingly. Here is how py.test
|
||||
calls the test function:
|
||||
|
||||
1. py.test discovers the ``test_function`` because of the ``test_`` prefix.
|
||||
The test function needs a function argument named ``myfuncarg``.
|
||||
A matching factory function is discovered by looking for the
|
||||
name ``pytest_funcarg__myfuncarg``.
|
||||
|
||||
2. ``pytest_funcarg__myfuncarg(request)`` is called and
|
||||
returns the value for ``myfuncarg``.
|
||||
|
||||
3. ``test_function(42)`` call is executed.
|
||||
|
||||
Note that if you misspell a function argument or want
|
||||
to use one that isn't available, you'll see an error
|
||||
with a list of available function arguments. You can
|
||||
also issue::
|
||||
|
||||
py.test --funcargs test_simplefactory.py
|
||||
|
||||
to see available function arguments (which you can also
|
||||
think of as "resources").
|
||||
|
||||
Factory functions receive a `request object`_
|
||||
which they can use to register setup/teardown
|
||||
functions or access meta data about a test.
|
||||
|
||||
.. _`request object`:
|
||||
|
||||
funcarg factory request objects
|
||||
------------------------------------------
|
||||
|
||||
Request objects represents a handle on a specific python test function call. A request object is passed to a funcarg factory and provides access to test configuration and context as well as some `useful caching and finalization helpers`_. Here is a list of attributes:
|
||||
|
||||
``request.function``: python function object requesting the argument
|
||||
|
||||
``request.cls``: class object where the test function is defined in or None.
|
||||
|
||||
``request.module``: module object where the test function is defined in.
|
||||
|
||||
``request.config``: access to command line opts and general config
|
||||
|
||||
``request.param``: if exists was passed by a previous `metafunc.addcall`_
|
||||
|
||||
.. _`useful caching and finalization helpers`:
|
||||
|
||||
|
||||
registering funcarg related finalizers/cleanup
|
||||
----------------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def addfinalizer(func):
|
||||
""" call a finalizer function when test function finishes. """
|
||||
|
||||
Calling ``request.addfinalizer()`` is useful for scheduling teardown
|
||||
functions. Here is an example for providing a ``myfile``
|
||||
object that is to be closed when the execution of a
|
||||
test function finishes.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_funcarg__myfile(self, request):
|
||||
# ... create and open a unique per-function "myfile" object ...
|
||||
request.addfinalizer(lambda: myfile.close())
|
||||
return myfile
|
||||
|
||||
|
||||
managing fixtures across test modules and test runs
|
||||
----------------------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def cached_setup(setup, teardown=None, scope="module", extrakey=None):
|
||||
""" cache and return result of calling setup().
|
||||
|
||||
The requested argument name, the scope and the ``extrakey``
|
||||
determine the cache key. The scope also determines when
|
||||
teardown(result) will be called. valid scopes are:
|
||||
scope == 'function': when the single test function run finishes.
|
||||
scope == 'module': when tests in a different module are run
|
||||
scope == 'session': when tests of the session have run.
|
||||
"""
|
||||
|
||||
Calling ``request.cached_setup()`` helps you to manage fixture
|
||||
objects across several scopes. For example, for creating a Database object
|
||||
that is to be setup only once during a test session you can use the helper
|
||||
like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_funcarg__database(request):
|
||||
return request.cached_setup(
|
||||
setup=lambda: Database("..."),
|
||||
teardown=lambda val: val.close(),
|
||||
scope="session"
|
||||
)
|
||||
|
||||
dynamically applying a marker
|
||||
---------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def applymarker(self, marker):
|
||||
""" apply a marker to a test function invocation.
|
||||
|
||||
The 'marker' must be created with py.test.mark.* XYZ.
|
||||
"""
|
||||
|
||||
``request.applymarker(marker)`` will mark the test invocation
|
||||
with the given marker. For example, if your funcarg factory provides
|
||||
values which may cause a test function to fail you can call
|
||||
``request.applymarker(py.test.mark.xfail(reason='flaky config'))``
|
||||
and this will cause the test to not show tracebacks. See xfail_
|
||||
for details.
|
||||
|
||||
.. _`xfail`: plugin/skipping.html#xfail
|
||||
|
||||
requesting values of other funcargs
|
||||
---------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def getfuncargvalue(name):
|
||||
""" Lookup and call function argument factory for the given name.
|
||||
Each function argument is only created once per function setup.
|
||||
"""
|
||||
|
||||
``request.getfuncargvalue(name)`` calls another funcarg factory function.
|
||||
You can use this function if you want to `decorate a funcarg`_, i.e.
|
||||
you want to provide the "normal" value but add something
|
||||
extra. If a factory cannot be found a ``request.Error``
|
||||
exception will be raised.
|
||||
|
||||
.. _`test generators`:
|
||||
.. _`parametrizing-tests`:
|
||||
|
||||
generating parametrized tests
|
||||
===========================================================
|
||||
|
||||
You can parametrize multiple runs of the same test
|
||||
function by adding new test function calls with different
|
||||
function argument values. Let's look at a simple self-contained
|
||||
example:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# ./test_example.py
|
||||
def pytest_generate_tests(metafunc):
|
||||
if "numiter" in metafunc.funcargnames:
|
||||
for i in range(10):
|
||||
metafunc.addcall(funcargs=dict(numiter=i))
|
||||
|
||||
def test_func(numiter):
|
||||
assert numiter < 9
|
||||
|
||||
If you run this with ``py.test test_example.py`` you'll get:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
============================= test session starts ==========================
|
||||
python: platform linux2 -- Python 2.6.2
|
||||
test object 1: /home/hpk/hg/py/trunk/test_example.py
|
||||
|
||||
test_example.py .........F
|
||||
|
||||
================================ FAILURES ==================================
|
||||
__________________________ test_func.test_func[9] __________________________
|
||||
|
||||
numiter = 9
|
||||
|
||||
def test_func(numiter):
|
||||
> assert numiter < 9
|
||||
E assert 9 < 9
|
||||
|
||||
/home/hpk/hg/py/trunk/test_example.py:10: AssertionError
|
||||
|
||||
|
||||
Here is what happens in detail:
|
||||
|
||||
1. ``pytest_generate_tests(metafunc)`` hook is called once for each test
|
||||
function. It adds ten new function calls with explicit function arguments.
|
||||
|
||||
2. **execute tests**: ``test_func(numiter)`` is called ten times with
|
||||
ten different arguments.
|
||||
|
||||
.. _`metafunc object`:
|
||||
|
||||
test generators and metafunc objects
|
||||
-------------------------------------------
|
||||
|
||||
metafunc objects are passed to the ``pytest_generate_tests`` hook.
|
||||
They help to inspect a testfunction and to generate tests
|
||||
according to test configuration or values specified
|
||||
in the class or module where a test function is defined:
|
||||
|
||||
``metafunc.funcargnames``: set of required function arguments for given function
|
||||
|
||||
``metafunc.function``: underlying python test function
|
||||
|
||||
``metafunc.cls``: class object where the test function is defined in or None.
|
||||
|
||||
``metafunc.module``: the module object where the test function is defined in.
|
||||
|
||||
``metafunc.config``: access to command line opts and general config
|
||||
|
||||
|
||||
.. _`metafunc.addcall`:
|
||||
|
||||
the ``metafunc.addcall()`` method
|
||||
-----------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def addcall(funcargs={}, id=None, param=None):
|
||||
""" trigger a new test function call. """
|
||||
|
||||
``funcargs`` can be a dictionary of argument names
|
||||
mapped to values - providing it is called *direct parametrization*.
|
||||
|
||||
If you provide an `id`` it will be used for reporting
|
||||
and identification purposes. If you don't supply an `id`
|
||||
the stringified counter of the list of added calls will be used.
|
||||
``id`` values needs to be unique between all
|
||||
invocations for a given test function.
|
||||
|
||||
``param`` if specified will be seen by any
|
||||
`funcarg factory`_ as a ``request.param`` attribute.
|
||||
Setting it is called *indirect parametrization*.
|
||||
|
||||
Indirect parametrization is preferable if test values are
|
||||
expensive to setup or can only be created in certain environments.
|
||||
Test generators and thus ``addcall()`` invocations are performed
|
||||
during test collection which is separate from the actual test
|
||||
setup and test run phase. With distributed testing collection
|
||||
and test setup/run happens in different process.
|
||||
|
||||
|
||||
|
||||
.. _`tutorial examples`:
|
||||
|
||||
Tutorial Examples
|
||||
=======================================
|
||||
|
||||
To see how you can implement custom paramtrization schemes,
|
||||
see e.g. `parametrizing tests, generalized`_ (blog post).
|
||||
|
||||
To enable creation of test support code that can flexibly
|
||||
register setup/teardown functions see the `blog post about
|
||||
the monkeypatch funcarg`_.
|
||||
|
||||
If you find issues or have further suggestions for improving
|
||||
the mechanism you are welcome to checkout `contact possibilities`_ page.
|
||||
|
||||
.. _`application setup tutorial example`:
|
||||
.. _appsetup:
|
||||
@@ -165,7 +442,7 @@ and to offer a new mysetup method:
|
||||
host = self.config.option.ssh
|
||||
if host is None:
|
||||
py.test.skip("specify ssh host with --ssh")
|
||||
return py.execnet.SshGateway(host)
|
||||
return execnet.SshGateway(host)
|
||||
|
||||
|
||||
Now any test function can use the ``mysetup.getsshconnection()`` method like this:
|
||||
@@ -274,261 +551,3 @@ methods in a convenient way.
|
||||
|
||||
.. _`py.path.local`: ../path.html#local
|
||||
.. _`conftest plugin`: customize.html#conftestplugin
|
||||
|
||||
.. _`funcarg factory`:
|
||||
|
||||
funcarg factories: setting up test function arguments
|
||||
==============================================================
|
||||
|
||||
Test functions can specify one ore more arguments ("funcargs")
|
||||
and a test module or plugin can define functions that provide
|
||||
the function argument. Let's look at a simple self-contained
|
||||
example that you can put into a test module:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# ./test_simplefactory.py
|
||||
def pytest_funcarg__myfuncarg(request):
|
||||
return 42
|
||||
|
||||
def test_function(myfuncarg):
|
||||
assert myfuncarg == 17
|
||||
|
||||
If you run this with ``py.test test_simplefactory.py`` you see something like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
=========================== test session starts ============================
|
||||
python: platform linux2 -- Python 2.6.2
|
||||
test object 1: /home/hpk/hg/py/trunk/example/funcarg/test_simplefactory.py
|
||||
|
||||
test_simplefactory.py F
|
||||
|
||||
================================ FAILURES ==================================
|
||||
______________________________ test_function _______________________________
|
||||
|
||||
myfuncarg = 42
|
||||
|
||||
def test_function(myfuncarg):
|
||||
> assert myfuncarg == 17
|
||||
E assert 42 == 17
|
||||
|
||||
test_simplefactory.py:6: AssertionError
|
||||
======================== 1 failed in 0.11 seconds ==========================
|
||||
|
||||
|
||||
This means that the test function got executed and the assertion failed.
|
||||
Here is how py.test comes to execute this test function:
|
||||
|
||||
1. py.test discovers the ``test_function`` because of the ``test_`` prefix.
|
||||
The test function needs a function argument named ``myfuncarg``.
|
||||
A matching factory function is discovered by looking for the special
|
||||
name ``pytest_funcarg__myfuncarg``.
|
||||
|
||||
2. ``pytest_funcarg__myfuncarg(request)`` is called and
|
||||
returns the value for ``myfuncarg``.
|
||||
|
||||
3. ``test_function(42)`` call is executed.
|
||||
|
||||
Note that if you misspell a function argument or want
|
||||
to use one that isn't available, an error with a list of
|
||||
available function argument is provided.
|
||||
|
||||
For more interesting factory functions that make good use of the
|
||||
`request object`_ please see the `application setup tutorial example`_.
|
||||
|
||||
.. _`request object`:
|
||||
|
||||
funcarg factory request objects
|
||||
------------------------------------------
|
||||
|
||||
Request objects are passed to funcarg factories and allow
|
||||
to access test configuration, test context and `useful caching
|
||||
and finalization helpers`_. Here is a list of attributes:
|
||||
|
||||
``request.function``: python function object requesting the argument
|
||||
|
||||
``request.cls``: class object where the test function is defined in or None.
|
||||
|
||||
``request.module``: module object where the test function is defined in.
|
||||
|
||||
``request.config``: access to command line opts and general config
|
||||
|
||||
``request.param``: if exists was passed by a previous `metafunc.addcall`_
|
||||
|
||||
.. _`useful caching and finalization helpers`:
|
||||
|
||||
|
||||
registering funcarg related finalizers/cleanup
|
||||
----------------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def addfinalizer(func):
|
||||
""" call a finalizer function when test function finishes. """
|
||||
|
||||
Calling ``request.addfinalizer()`` is useful for scheduling teardown
|
||||
functions. Here is an example for providing a ``myfile``
|
||||
object that is to be closed when the execution of a
|
||||
test function finishes.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_funcarg__myfile(self, request):
|
||||
# ... create and open a unique per-function "myfile" object ...
|
||||
request.addfinalizer(lambda: myfile.close())
|
||||
return myfile
|
||||
|
||||
|
||||
managing fixtures across test modules and test runs
|
||||
----------------------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def cached_setup(setup, teardown=None, scope="module", extrakey=None):
|
||||
""" cache and return result of calling setup().
|
||||
|
||||
The scope and the ``extrakey`` determine the cache key.
|
||||
The scope also determines when teardown(result)
|
||||
will be called. valid scopes are:
|
||||
scope == 'function': when the single test function run finishes.
|
||||
scope == 'module': when tests in a different module are run
|
||||
scope == 'session': when tests of the session have run.
|
||||
"""
|
||||
|
||||
Calling ``request.cached_setup()`` helps you to manage fixture
|
||||
objects across several scopes. For example, for creating a Database object
|
||||
that is to be setup only once during a test session you can use the helper
|
||||
like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def pytest_funcarg__database(request):
|
||||
return request.cached_setup(
|
||||
setup=lambda: Database("..."),
|
||||
teardown=lambda val: val.close(),
|
||||
scope="session"
|
||||
)
|
||||
|
||||
|
||||
requesting values of other funcargs
|
||||
---------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def getfuncargvalue(name):
|
||||
""" Lookup and call function argument factory for the given name.
|
||||
Each function argument is only created once per function setup.
|
||||
"""
|
||||
|
||||
``request.getfuncargvalue(name)`` calls another funcarg factory function.
|
||||
You can use this function if you want to `decorate a funcarg`_, i.e.
|
||||
you want to provide the "normal" value but add something
|
||||
extra. If a factory cannot be found a ``request.Error``
|
||||
exception will be raised.
|
||||
|
||||
.. _`test generators`:
|
||||
.. _`parametrizing-tests`:
|
||||
|
||||
generating parametrized tests
|
||||
===========================================================
|
||||
|
||||
You can parametrize multiple runs of the same test
|
||||
function by adding new test function calls with different
|
||||
function argument values. Let's look at a simple self-contained
|
||||
example:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# ./test_example.py
|
||||
def pytest_generate_tests(metafunc):
|
||||
if "numiter" in metafunc.funcargnames:
|
||||
for i in range(10):
|
||||
metafunc.addcall(funcargs=dict(numiter=i))
|
||||
|
||||
def test_func(numiter):
|
||||
assert numiter < 9
|
||||
|
||||
If you run this with ``py.test test_example.py`` you'll get:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
============================= test session starts ==========================
|
||||
python: platform linux2 -- Python 2.6.2
|
||||
test object 1: /home/hpk/hg/py/trunk/test_example.py
|
||||
|
||||
test_example.py .........F
|
||||
|
||||
================================ FAILURES ==================================
|
||||
__________________________ test_func.test_func[9] __________________________
|
||||
|
||||
numiter = 9
|
||||
|
||||
def test_func(numiter):
|
||||
> assert numiter < 9
|
||||
E assert 9 < 9
|
||||
|
||||
/home/hpk/hg/py/trunk/test_example.py:10: AssertionError
|
||||
|
||||
|
||||
Here is what happens in detail:
|
||||
|
||||
1. ``pytest_generate_tests(metafunc)`` hook is called once for each test
|
||||
function. It adds ten new function calls with explicit function arguments.
|
||||
|
||||
2. **execute tests**: ``test_func(numiter)`` is called ten times with
|
||||
ten different arguments.
|
||||
|
||||
.. _`metafunc object`:
|
||||
|
||||
test generators and metafunc objects
|
||||
-------------------------------------------
|
||||
|
||||
metafunc objects are passed to the ``pytest_generate_tests`` hook.
|
||||
They help to inspect a testfunction and to generate tests
|
||||
according to test configuration or values specified
|
||||
in the class or module where a test function is defined:
|
||||
|
||||
``metafunc.funcargnames``: set of required function arguments for given function
|
||||
|
||||
``metafunc.function``: underlying python test function
|
||||
|
||||
``metafunc.cls``: class object where the test function is defined in or None.
|
||||
|
||||
``metafunc.module``: the module object where the test function is defined in.
|
||||
|
||||
``metafunc.config``: access to command line opts and general config
|
||||
|
||||
|
||||
.. _`metafunc.addcall`:
|
||||
|
||||
the ``metafunc.addcall()`` method
|
||||
-----------------------------------------------
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def addcall(funcargs={}, id=None, param=None):
|
||||
""" trigger a new test function call. """
|
||||
|
||||
``funcargs`` can be a dictionary of argument names
|
||||
mapped to values - providing it is called *direct parametrization*.
|
||||
|
||||
If you provide an `id`` it will be used for reporting
|
||||
and identification purposes. If you don't supply an `id`
|
||||
the stringified counter of the list of added calls will be used.
|
||||
``id`` values needs to be unique between all
|
||||
invocations for a given test function.
|
||||
|
||||
``param`` if specified will be seen by any
|
||||
`funcarg factory`_ as a ``request.param`` attribute.
|
||||
Setting it is called *indirect parametrization*.
|
||||
|
||||
Indirect parametrization is preferable if test values are
|
||||
expensive to setup or can only be created in certain environments.
|
||||
Test generators and thus ``addcall()`` invocations are performed
|
||||
during test collection which is separate from the actual test
|
||||
setup and test run phase. With distributed testing collection
|
||||
and test setup/run happens in different process.
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user