Compare commits
478 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52011e84d3 | ||
|
|
de583ed7a3 | ||
|
|
f8480caae4 | ||
|
|
47d9e6ca1f | ||
|
|
6f0a33dfdc | ||
|
|
1641d00cb1 | ||
|
|
2d7a32f7ea | ||
|
|
06acbb9f5e | ||
|
|
7b630d9080 | ||
|
|
98de64badc | ||
|
|
1e241e1f2a | ||
|
|
ff2c18fedb | ||
|
|
bf64a800d6 | ||
|
|
efc57391eb | ||
|
|
dc65aa1fea | ||
|
|
80ad3fb8ed | ||
|
|
bc7110931a | ||
|
|
8c508612ec | ||
|
|
b7d046527e | ||
|
|
42804c52e8 | ||
|
|
d88a3712c5 | ||
|
|
abfedd692e | ||
|
|
d0b048c86d | ||
|
|
b6f069f4c3 | ||
|
|
edc2e5ab82 | ||
|
|
6da9a087f8 | ||
|
|
e19462d581 | ||
|
|
a811fabb43 | ||
|
|
07e76cbef2 | ||
|
|
76d5c9e4f4 | ||
|
|
abcadc4202 | ||
|
|
b7f6a9f3fd | ||
|
|
c8264385ea | ||
|
|
ad8131be9e | ||
|
|
54c88a6cf3 | ||
|
|
115f15600f | ||
|
|
65a145e2a7 | ||
|
|
5719a72eeb | ||
|
|
4bc4495115 | ||
|
|
c66e9f8f0f | ||
|
|
52eafdc21e | ||
|
|
85c0d5481b | ||
|
|
fd9055fd11 | ||
|
|
780bdda95a | ||
|
|
fd4b461290 | ||
|
|
040062e40c | ||
|
|
d853e9167a | ||
|
|
d50ad270f0 | ||
|
|
30c93701a7 | ||
|
|
b507e1754c | ||
|
|
748fce94fd | ||
|
|
d6281b4206 | ||
|
|
b61ed2cf7e | ||
|
|
9263f30c88 | ||
|
|
8f9a88ef7a | ||
|
|
c64af0d9ce | ||
|
|
9181df42da | ||
|
|
74e1a49dd7 | ||
|
|
468b1241a5 | ||
|
|
24744cf5cf | ||
|
|
ffc969b6c2 | ||
|
|
0567a8ee77 | ||
|
|
580c8525f0 | ||
|
|
d6010aa0c9 | ||
|
|
c46e2cbbc7 | ||
|
|
c47835f5ec | ||
|
|
412b56f7cf | ||
|
|
faba432996 | ||
|
|
2ba23e8d08 | ||
|
|
d74f852fd6 | ||
|
|
1728798e81 | ||
|
|
53a8d20d88 | ||
|
|
61446faa17 | ||
|
|
9711e335d9 | ||
|
|
080a9d2f12 | ||
|
|
15af7e1662 | ||
|
|
e42cbc714f | ||
|
|
5e26e6e553 | ||
|
|
d0a4d348fe | ||
|
|
494be731e3 | ||
|
|
8ae244a06a | ||
|
|
f91049cec9 | ||
|
|
270d0f89ba | ||
|
|
e382ed4245 | ||
|
|
ef7cb47b1e | ||
|
|
6efde60b8b | ||
|
|
fd059359cc | ||
|
|
c2c504797e | ||
|
|
84f9f45f98 | ||
|
|
28aa4c891e | ||
|
|
6ff0fdb977 | ||
|
|
b0837693d0 | ||
|
|
52851e4388 | ||
|
|
cbe31f3748 | ||
|
|
c9bbdf4f10 | ||
|
|
f984e94fca | ||
|
|
b4fe91943d | ||
|
|
7d6317802e | ||
|
|
0365e5c3a0 | ||
|
|
e6859406f1 | ||
|
|
51cff6f106 | ||
|
|
68e58e1493 | ||
|
|
9f7eac0ba1 | ||
|
|
b0e31dca86 | ||
|
|
7d10a57514 | ||
|
|
2c0f6207e9 | ||
|
|
adb12d0d4f | ||
|
|
844c141d10 | ||
|
|
02d94e69f0 | ||
|
|
1bc56f9838 | ||
|
|
98ea8fae32 | ||
|
|
36288c5134 | ||
|
|
83a3cc9c94 | ||
|
|
0c04b44919 | ||
|
|
a5e8860feb | ||
|
|
8d95f89a6a | ||
|
|
3bca62e9e4 | ||
|
|
72b4534a0c | ||
|
|
21b4280126 | ||
|
|
30a9debaf1 | ||
|
|
4c5718c78d | ||
|
|
c93b949878 | ||
|
|
cf34adb75f | ||
|
|
f824a73143 | ||
|
|
e45a33f029 | ||
|
|
064e79761c | ||
|
|
f7713c47e8 | ||
|
|
8e4e2ba244 | ||
|
|
3b8935c533 | ||
|
|
ce8678e6d5 | ||
|
|
2e1f6c85f6 | ||
|
|
ca5e6830c6 | ||
|
|
69cbac8fb5 | ||
|
|
7301981f32 | ||
|
|
555ba4159d | ||
|
|
f47ae74981 | ||
|
|
e061ace099 | ||
|
|
85d52481b1 | ||
|
|
47379d4a79 | ||
|
|
0cb9d26d83 | ||
|
|
95cc114b34 | ||
|
|
9d716a39d6 | ||
|
|
923dcfd620 | ||
|
|
b5467645d3 | ||
|
|
a65380941d | ||
|
|
17d7c60735 | ||
|
|
81f822d528 | ||
|
|
0b340aa1f6 | ||
|
|
859915dc5e | ||
|
|
a8f4f49a82 | ||
|
|
e18c3ed494 | ||
|
|
2263fcf6b7 | ||
|
|
b96d552dbd | ||
|
|
2b2c1e5b7b | ||
|
|
7b63fa5966 | ||
|
|
b18040337a | ||
|
|
42e0d7970c | ||
|
|
ed0a4fe23b | ||
|
|
bb0632c7ad | ||
|
|
84ab194516 | ||
|
|
530cae9204 | ||
|
|
9dc43e84dc | ||
|
|
8af265da04 | ||
|
|
e3b9382122 | ||
|
|
c3f4eb6d57 | ||
|
|
892aa457be | ||
|
|
8f7b53e55b | ||
|
|
d27c377817 | ||
|
|
50abe43216 | ||
|
|
ddc67ca13a | ||
|
|
a1d3da4027 | ||
|
|
85e7b11ef5 | ||
|
|
77e1f93ca1 | ||
|
|
94b1ce65c6 | ||
|
|
f5b992f68a | ||
|
|
24a458b4c8 | ||
|
|
ac1d277225 | ||
|
|
9777703e03 | ||
|
|
f43cda9681 | ||
|
|
b47fdbe0a7 | ||
|
|
cde970be69 | ||
|
|
cfd43a9b02 | ||
|
|
ebd10aa6b4 | ||
|
|
24db492f53 | ||
|
|
1b387bea62 | ||
|
|
9528b64f7f | ||
|
|
b96559149c | ||
|
|
3388d82c1c | ||
|
|
9985a7cdca | ||
|
|
1d00c5e109 | ||
|
|
0559f11aa5 | ||
|
|
2893cddb68 | ||
|
|
6910641266 | ||
|
|
4b81a07303 | ||
|
|
7eb765578a | ||
|
|
e2cf3e0932 | ||
|
|
1830de2c13 | ||
|
|
25ab906b8b | ||
|
|
8a3b4b9c37 | ||
|
|
2b8a54d5d9 | ||
|
|
4eabfed651 | ||
|
|
505a34bb85 | ||
|
|
2dade6ed00 | ||
|
|
825ea9bfa1 | ||
|
|
cc1186242c | ||
|
|
86284689a3 | ||
|
|
c70d020bf3 | ||
|
|
4622c28ffd | ||
|
|
899998cf9c | ||
|
|
6f385fb4ea | ||
|
|
18e12cbd67 | ||
|
|
3cf4e133cc | ||
|
|
d53bfe0aa7 | ||
|
|
97da43d909 | ||
|
|
ee080ce8a5 | ||
|
|
741a3e8602 | ||
|
|
177637bfb9 | ||
|
|
ae64221c34 | ||
|
|
22017f11d0 | ||
|
|
c1eaad7d57 | ||
|
|
8e6d538a57 | ||
|
|
0836d74ae7 | ||
|
|
a9e5bd52d3 | ||
|
|
b4b5e358c3 | ||
|
|
4190809d1a | ||
|
|
4e2a820c6a | ||
|
|
14a43fffee | ||
|
|
245d39f52d | ||
|
|
ac0b862f8f | ||
|
|
c41db8ebbb | ||
|
|
75c124ea17 | ||
|
|
ab13c3f362 | ||
|
|
c9af19dae1 | ||
|
|
ffffac27f9 | ||
|
|
618bd56acf | ||
|
|
402232e60f | ||
|
|
400b51caf6 | ||
|
|
9aaf0fd340 | ||
|
|
8976b3ee0e | ||
|
|
ac2f2b1deb | ||
|
|
0be961a0f3 | ||
|
|
b4a397d153 | ||
|
|
d1a9ab3df0 | ||
|
|
0ede968ec0 | ||
|
|
5f21abc3a3 | ||
|
|
e2bb81124c | ||
|
|
ea18e9656b | ||
|
|
3cc58c2f78 | ||
|
|
0ac94134f5 | ||
|
|
c142f2551d | ||
|
|
cccfaa81fb | ||
|
|
d960fb78fc | ||
|
|
d02d0bb7b7 | ||
|
|
fe4cdd8a90 | ||
|
|
54a143e6a8 | ||
|
|
3a4f69734a | ||
|
|
fbdc6e8cc0 | ||
|
|
c2c44f0ffc | ||
|
|
e12fe64b54 | ||
|
|
5240252164 | ||
|
|
1ffc006363 | ||
|
|
836232e544 | ||
|
|
2539e5a352 | ||
|
|
8e457338ee | ||
|
|
d92ee8c3c3 | ||
|
|
492c60c202 | ||
|
|
86ef81454f | ||
|
|
675669c52e | ||
|
|
92c5d395a2 | ||
|
|
fa4ea1d9e8 | ||
|
|
657a395839 | ||
|
|
9fb2079458 | ||
|
|
83620ced2e | ||
|
|
1fb824cd28 | ||
|
|
0d35994fb8 | ||
|
|
3a37f33d99 | ||
|
|
ddfb2d5f3a | ||
|
|
d81c0e9a92 | ||
|
|
048cb71bf6 | ||
|
|
903fd144ff | ||
|
|
d51e27a5cb | ||
|
|
064290a606 | ||
|
|
e6ae68c0cc | ||
|
|
43f970ab6b | ||
|
|
49b9f9091a | ||
|
|
99277be25f | ||
|
|
699892bd03 | ||
|
|
70c1503afc | ||
|
|
ee5d2eb696 | ||
|
|
2058f11931 | ||
|
|
53a9ee21d4 | ||
|
|
04118a5761 | ||
|
|
c101c30690 | ||
|
|
0e664d3471 | ||
|
|
fcb1749f10 | ||
|
|
180eb098f1 | ||
|
|
cc9b3ec296 | ||
|
|
66bd71a5d7 | ||
|
|
3365907989 | ||
|
|
6b2040f98d | ||
|
|
90551c6ce2 | ||
|
|
d3b8390df3 | ||
|
|
9554fe0bf8 | ||
|
|
5a13f31bce | ||
|
|
41bddb48a1 | ||
|
|
b820cf2e39 | ||
|
|
fd8638652d | ||
|
|
01ae5dbb2e | ||
|
|
b4797d6295 | ||
|
|
c9195a0f45 | ||
|
|
86c56c829a | ||
|
|
ef023ebad3 | ||
|
|
0c737e3de0 | ||
|
|
1b7c70eab4 | ||
|
|
6a5456f873 | ||
|
|
28b1079548 | ||
|
|
fe01d1b0df | ||
|
|
54174c308f | ||
|
|
901f764825 | ||
|
|
c1759fc384 | ||
|
|
e843b028e6 | ||
|
|
305cbecb34 | ||
|
|
a986b3fb4a | ||
|
|
fd42133d89 | ||
|
|
d6d7f3821f | ||
|
|
e79b43eeb2 | ||
|
|
afba6ce907 | ||
|
|
d5948325d4 | ||
|
|
b9b44bb87c | ||
|
|
00b00ff931 | ||
|
|
6f54a6be1e | ||
|
|
d1faccb061 | ||
|
|
0f6bb3b3ef | ||
|
|
542b87fed3 | ||
|
|
33f1df2369 | ||
|
|
041a12fdf2 | ||
|
|
555999397b | ||
|
|
25a45f6bf7 | ||
|
|
82f017edeb | ||
|
|
b4842b20f6 | ||
|
|
e14e966da9 | ||
|
|
ebe0c34a02 | ||
|
|
7d711083ec | ||
|
|
0a4d5a423e | ||
|
|
b9ccb9d31f | ||
|
|
a056b41070 | ||
|
|
6d26c44895 | ||
|
|
1644cd2da5 | ||
|
|
98135a3d30 | ||
|
|
307a41339c | ||
|
|
72ebd74715 | ||
|
|
bfa53811d3 | ||
|
|
0fa77d58c4 | ||
|
|
fa80b8ad17 | ||
|
|
9cdb6fc724 | ||
|
|
cd8e69e33c | ||
|
|
7b87f7b6b5 | ||
|
|
dd0da4643a | ||
|
|
7766526992 | ||
|
|
5c3d692008 | ||
|
|
bdf9147ad4 | ||
|
|
ad2ac256de | ||
|
|
a4466342ae | ||
|
|
0d7af592c0 | ||
|
|
66ffc5e0f8 | ||
|
|
320137a4aa | ||
|
|
0278dc9b6f | ||
|
|
7d9297e929 | ||
|
|
9e03ea8215 | ||
|
|
60f5b15f20 | ||
|
|
e67047d629 | ||
|
|
10edfa65dc | ||
|
|
daec4c70b8 | ||
|
|
dbfbc2b222 | ||
|
|
426907eafb | ||
|
|
4f0879ff9b | ||
|
|
bec6ee5c29 | ||
|
|
23fa4cec61 | ||
|
|
4b9dbd3920 | ||
|
|
98c6ced46e | ||
|
|
cb485e5af4 | ||
|
|
817b175870 | ||
|
|
bd320951e6 | ||
|
|
0cfd873abe | ||
|
|
d30ad3f5ce | ||
|
|
5dbf4fc0c2 | ||
|
|
e3a945a0b5 | ||
|
|
a5c075c4e2 | ||
|
|
c0dd7c5975 | ||
|
|
4031588811 | ||
|
|
1dc2a45cb2 | ||
|
|
40b172ca5a | ||
|
|
94031f9cef | ||
|
|
a6783cd6f3 | ||
|
|
438d85b5ad | ||
|
|
90b6ccd321 | ||
|
|
db778fd456 | ||
|
|
08f3a0791d | ||
|
|
663f824fc4 | ||
|
|
2700a94d49 | ||
|
|
e31f40c2d0 | ||
|
|
fc073cb81c | ||
|
|
2e90aaf7af | ||
|
|
238b890d9b | ||
|
|
49119e31bf | ||
|
|
bb5f1e8173 | ||
|
|
05fbd490da | ||
|
|
5322f057a0 | ||
|
|
086cc03f05 | ||
|
|
d67514b657 | ||
|
|
a467fbea0d | ||
|
|
6686c67a41 | ||
|
|
73f36fc8b7 | ||
|
|
bd8a2cc18c | ||
|
|
6d1b7e94d1 | ||
|
|
9eff939b02 | ||
|
|
0a8b27ff49 | ||
|
|
72752165df | ||
|
|
9b21d3f206 | ||
|
|
dde0a81677 | ||
|
|
31576fac61 | ||
|
|
82846777a7 | ||
|
|
7f49e0fddc | ||
|
|
eda8b02a8d | ||
|
|
1fd1617427 | ||
|
|
97252a8b66 | ||
|
|
4a1cc792c9 | ||
|
|
581b3a110c | ||
|
|
e118682db1 | ||
|
|
4eeb1c4f31 | ||
|
|
e2c4730e17 | ||
|
|
c3e844e561 | ||
|
|
fde947e1a8 | ||
|
|
ce0af892aa | ||
|
|
3f389238f8 | ||
|
|
1faf95273c | ||
|
|
ba5d4ae42f | ||
|
|
d18124f5ed | ||
|
|
846cf781a1 | ||
|
|
85dd51ccc8 | ||
|
|
ded88700a3 | ||
|
|
a9d1f40c29 | ||
|
|
e2d19aab39 | ||
|
|
7210e443ee | ||
|
|
f674c57d1a | ||
|
|
9dec27371d | ||
|
|
75328b66e6 | ||
|
|
cf9d345382 | ||
|
|
0d8392bc45 | ||
|
|
47d2d20d81 | ||
|
|
b0a5740898 | ||
|
|
bc8c4b3ebd | ||
|
|
2eebe6c677 | ||
|
|
8c326c5e66 | ||
|
|
8e9034f074 | ||
|
|
612fb96d02 | ||
|
|
49d067d72e | ||
|
|
8e1301b6d7 | ||
|
|
8ac5af2896 | ||
|
|
8550ea0728 | ||
|
|
6e619e0a70 | ||
|
|
5b2b71bfd4 | ||
|
|
d92322a574 | ||
|
|
7e793b9419 | ||
|
|
d81b703f10 | ||
|
|
1265cb9952 | ||
|
|
124e58e42d | ||
|
|
2697b63bcd | ||
|
|
ee5b836e27 | ||
|
|
a4c17dfb19 | ||
|
|
00c0d62c9b | ||
|
|
a5d4c20905 | ||
|
|
0335c6d750 | ||
|
|
8b6e42317b | ||
|
|
56e6ae567c | ||
|
|
33b663e03d | ||
|
|
4bfbe7ec22 | ||
|
|
cf37c477bb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,7 +13,6 @@ include/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.swp
|
||||
*.html
|
||||
*.class
|
||||
*.orig
|
||||
*~
|
||||
|
||||
@@ -9,6 +9,7 @@ lib/
|
||||
bin/
|
||||
include/
|
||||
.Python/
|
||||
.env/
|
||||
|
||||
# These lines are suggested according to the svn:ignore property
|
||||
# Feel free to enable them by uncommenting them
|
||||
@@ -27,6 +28,7 @@ dist/
|
||||
*.egg-info
|
||||
issue/
|
||||
env/
|
||||
env3/
|
||||
3rdparty/
|
||||
.tox
|
||||
.cache
|
||||
|
||||
5
.hgtags
5
.hgtags
@@ -62,3 +62,8 @@ b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14
|
||||
0000000000000000000000000000000000000000 1.4.14
|
||||
af860de70cc3f157ac34ca1d4bf557a057bff775 2.4.0
|
||||
8828c924acae0b4cad2e2cb92943d51da7cb744a 2.4.1
|
||||
8d051f89184bfa3033f5e59819dff9f32a612941 2.4.2
|
||||
a064ad64d167508a8e9e73766b1a4e6bd10c85db 2.5.0
|
||||
039d543d1ca02a716c0b0de9a7131beb8021e8a2 2.5.1
|
||||
421d3b4d150d901de24b1cbeb8955547b1420483 2.5.2
|
||||
60725b17a9d1af4100abb8be3f9f4ddf6262bf34 2.6.0
|
||||
|
||||
@@ -2,7 +2,8 @@ language: python
|
||||
# command to install dependencies
|
||||
install: "pip install -U detox"
|
||||
# # command to run tests
|
||||
script: detox --recreate
|
||||
script: detox --recreate -i ALL=https://devpi.net/hpk/dev/
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
- "chat.freenode.net#pytest-dev"
|
||||
|
||||
18
AUTHORS
18
AUTHORS
@@ -1,7 +1,7 @@
|
||||
Holger Krekel, holger at merlinux eu
|
||||
merlinux GmbH, Germany, office at merlinux eu
|
||||
|
||||
Contributors include::
|
||||
Contributors include::
|
||||
|
||||
Ronny Pfannschmidt
|
||||
Benjamin Peterson
|
||||
@@ -9,21 +9,21 @@ Floris Bruynooghe
|
||||
Jason R. Coombs
|
||||
Wouter van Ackooy
|
||||
Samuele Pedroni
|
||||
Anatoly Bubenkoff
|
||||
Anatoly Bubenkoff
|
||||
Brianna Laugher
|
||||
Carl Friedrich Bolz
|
||||
Armin Rigo
|
||||
Maho
|
||||
Jaap Broekhuizen
|
||||
Jaap Broekhuizen
|
||||
Maciek Fijalkowski
|
||||
Guido Wesdorp
|
||||
Brian Dorsey
|
||||
Ross Lawley
|
||||
Ralf Schmitt
|
||||
Chris Lamb
|
||||
Chris Lamb
|
||||
Harald Armin Massa
|
||||
Martijn Faassen
|
||||
Ian Bicking
|
||||
Ian Bicking
|
||||
Jan Balster
|
||||
Grig Gheorghiu
|
||||
Bob Ippolito
|
||||
@@ -35,3 +35,11 @@ Brian Okken
|
||||
Katarzyna Jachim
|
||||
Christian Theunert
|
||||
Anthon van der Neut
|
||||
Mark Abramowitz
|
||||
Piotr Banaszkiewicz
|
||||
Jurko Gospodnetić
|
||||
Marc Schlaich
|
||||
Christopher Gilling
|
||||
Daniel Grana
|
||||
Andy Freeland
|
||||
Trevor Bekolay
|
||||
|
||||
452
CHANGELOG
452
CHANGELOG
@@ -1,4 +1,294 @@
|
||||
Changes between 2.4.1 and 2.4.2
|
||||
NEXT (2.6)
|
||||
-----------------------------------
|
||||
|
||||
- Cache exceptions from fixtures according to their scope (issue 467).
|
||||
|
||||
- fix issue537: Avoid importing old assertion reinterpretation code by default.
|
||||
|
||||
- fix issue364: shorten and enhance tracebacks representation by default.
|
||||
The new "--tb=auto" option (default) will only display long tracebacks
|
||||
for the first and last entry. You can get the old behaviour of printing
|
||||
all entries as long entries with "--tb=long". Also short entries by
|
||||
default are now printed very similarly to "--tb=native" ones.
|
||||
|
||||
- fix issue514: teach assertion reinterpretation about private class attributes
|
||||
|
||||
- change -v output to include full node IDs of tests. Users can copy
|
||||
a node ID from a test run, including line number, and use it as a
|
||||
positional argument in order to run only a single test.
|
||||
|
||||
- fix issue 475: fail early and comprehensible if calling
|
||||
pytest.raises with wrong exception type.
|
||||
|
||||
- fix issue516: tell in getting-started about current dependencies.
|
||||
|
||||
- cleanup setup.py a bit and specify supported versions. Thanks Jurko
|
||||
Gospodnetic for the PR.
|
||||
|
||||
- change XPASS colour to yellow rather then red when tests are run
|
||||
with -v.
|
||||
|
||||
- fix issue473: work around mock putting an unbound method into a class
|
||||
dict when double-patching.
|
||||
|
||||
- fix issue498: if a fixture finalizer fails, make sure that
|
||||
the fixture is still invalidated.
|
||||
|
||||
- fix issue453: the result of the pytest_assertrepr_compare hook now gets
|
||||
it's newlines escaped so that format_exception does not blow up.
|
||||
|
||||
- internal new warning system: pytest will now produce warnings when
|
||||
it detects oddities in your test collection or execution.
|
||||
Warnings are ultimately sent to a new pytest_logwarning hook which is
|
||||
currently only implemented by the terminal plugin which displays
|
||||
warnings in the summary line and shows more details when -rw (report on
|
||||
warnings) is specified.
|
||||
|
||||
- change skips into warnings for test classes with an __init__ and
|
||||
callables in test modules which look like a test but are not functions.
|
||||
|
||||
- fix issue436: improved finding of initial conftest files from command
|
||||
line arguments by using the result of parse_known_args rather than
|
||||
the previous flaky heuristics. Thanks Marc Abramowitz for tests
|
||||
and initial fixing approaches in this area.
|
||||
|
||||
- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
|
||||
during collection/loading of test modules. Thanks to Marc Schlaich
|
||||
for the complete PR.
|
||||
|
||||
- fix issue490: include pytest_load_initial_conftests in documentation
|
||||
and improve docstring.
|
||||
|
||||
- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
|
||||
if it's triggered ahead of command line parsing.
|
||||
|
||||
- merge PR123: improved integration with mock.patch decorator on tests.
|
||||
|
||||
- fix issue412: messing with stdout/stderr FD-level streams is now
|
||||
captured without crashes.
|
||||
|
||||
- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR.
|
||||
|
||||
- improve example for pytest integration with "python setup.py test"
|
||||
which now has a generic "-a" or "--pytest-args" option where you
|
||||
can pass additional options as a quoted string. Thanks Trevor Bekolay.
|
||||
|
||||
- simplified internal capturing mechanism and made it more robust
|
||||
against tests or setups changing FD1/FD2, also better integrated
|
||||
now with pytest.pdb() in single tests.
|
||||
|
||||
- improvements to pytest's own test-suite leakage detection, courtesy of PRs
|
||||
from Marc Abramowitz
|
||||
|
||||
- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz.
|
||||
|
||||
- fix issue493: don't run tests in doc directory with ``python setup.py test``
|
||||
(use tox -e doctesting for that)
|
||||
|
||||
- fix issue486: better reporting and handling of early conftest loading failures
|
||||
|
||||
- some cleanup and simplification of internal conftest handling.
|
||||
|
||||
- work a bit harder to break reference cycles when catching exceptions.
|
||||
Thanks Jurko Gospodnetic.
|
||||
|
||||
- fix issue443: fix skip examples to use proper comparison. Thanks Alex
|
||||
Groenholm.
|
||||
|
||||
- support nose-style ``__test__`` attribute on modules, classes and
|
||||
functions, including unittest-style Classes. If set to False, the
|
||||
test will not be collected.
|
||||
|
||||
- fix issue512: show "<notset>" for arguments which might not be set
|
||||
in monkeypatch plugin. Improves output in documentation.
|
||||
|
||||
|
||||
2.5.2
|
||||
-----------------------------------
|
||||
|
||||
- fix issue409 -- better interoperate with cx_freeze by not
|
||||
trying to import from collections.abc which causes problems
|
||||
for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
|
||||
|
||||
- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
|
||||
Thanks Jurko Gospodnetic for the complete PR.
|
||||
|
||||
- fix issue425: mention at end of "py.test -h" that --markers
|
||||
and --fixtures work according to specified test path (or current dir)
|
||||
|
||||
- fix issue413: exceptions with unicode attributes are now printed
|
||||
correctly also on python2 and with pytest-xdist runs. (the fix
|
||||
requires py-1.4.20)
|
||||
|
||||
- copy, cleanup and integrate py.io capture
|
||||
from pylib 1.4.20.dev2 (rev 13d9af95547e)
|
||||
|
||||
- address issue416: clarify docs as to conftest.py loading semantics
|
||||
|
||||
- fix issue429: comparing byte strings with non-ascii chars in assert
|
||||
expressions now work better. Thanks Floris Bruynooghe.
|
||||
|
||||
- make capfd/capsys.capture private, its unused and shouldnt be exposed
|
||||
|
||||
|
||||
2.5.1
|
||||
-----------------------------------
|
||||
|
||||
- merge new documentation styling PR from Tobias Bieniek.
|
||||
|
||||
- fix issue403: allow parametrize of multiple same-name functions within
|
||||
a collection node. Thanks Andreas Kloeckner and Alex Gaynor for reporting
|
||||
and analysis.
|
||||
|
||||
- Allow parameterized fixtures to specify the ID of the parameters by
|
||||
adding an ids argument to pytest.fixture() and pytest.yield_fixture().
|
||||
Thanks Floris Bruynooghe.
|
||||
|
||||
- fix issue404 by always using the binary xml escape in the junitxml
|
||||
plugin. Thanks Ronny Pfannschmidt.
|
||||
|
||||
- fix issue407: fix addoption docstring to point to argparse instead of
|
||||
optparse. Thanks Daniel D. Wright.
|
||||
|
||||
|
||||
|
||||
2.5.0
|
||||
-----------------------------------
|
||||
|
||||
- dropped python2.5 from automated release testing of pytest itself
|
||||
which means it's probably going to break soon (but still works
|
||||
with this release we believe).
|
||||
|
||||
- simplified and fixed implementation for calling finalizers when
|
||||
parametrized fixtures or function arguments are involved. finalization
|
||||
is now performed lazily at setup time instead of in the "teardown phase".
|
||||
While this might sound odd at first, it helps to ensure that we are
|
||||
correctly handling setup/teardown even in complex code. User-level code
|
||||
should not be affected unless it's implementing the pytest_runtest_teardown
|
||||
hook and expecting certain fixture instances are torn down within (very
|
||||
unlikely and would have been unreliable anyway).
|
||||
|
||||
- PR90: add --color=yes|no|auto option to force terminal coloring
|
||||
mode ("auto" is default). Thanks Marc Abramowitz.
|
||||
|
||||
- fix issue319 - correctly show unicode in assertion errors. Many
|
||||
thanks to Floris Bruynooghe for the complete PR. Also means
|
||||
we depend on py>=1.4.19 now.
|
||||
|
||||
- fix issue396 - correctly sort and finalize class-scoped parametrized
|
||||
tests independently from number of methods on the class.
|
||||
|
||||
- refix issue323 in a better way -- parametrization should now never
|
||||
cause Runtime Recursion errors because the underlying algorithm
|
||||
for re-ordering tests per-scope/per-fixture is not recursive
|
||||
anymore (it was tail-call recursive before which could lead
|
||||
to problems for more than >966 non-function scoped parameters).
|
||||
|
||||
- fix issue290 - there is preliminary support now for parametrizing
|
||||
with repeated same values (sometimes useful to to test if calling
|
||||
a second time works as with the first time).
|
||||
|
||||
- close issue240 - document precisely how pytest module importing
|
||||
works, discuss the two common test directory layouts, and how it
|
||||
interacts with PEP420-namespace packages.
|
||||
|
||||
- fix issue246 fix finalizer order to be LIFO on independent fixtures
|
||||
depending on a parametrized higher-than-function scoped fixture.
|
||||
(was quite some effort so please bear with the complexity of this sentence :)
|
||||
Thanks Ralph Schmitt for the precise failure example.
|
||||
|
||||
- fix issue244 by implementing special index for parameters to only use
|
||||
indices for paramentrized test ids
|
||||
|
||||
- fix issue287 by running all finalizers but saving the exception
|
||||
from the first failing finalizer and re-raising it so teardown will
|
||||
still have failed. We reraise the first failing exception because
|
||||
it might be the cause for other finalizers to fail.
|
||||
|
||||
- fix ordering when mock.patch or other standard decorator-wrappings
|
||||
are used with test methods. This fixues issue346 and should
|
||||
help with random "xdist" collection failures. Thanks to
|
||||
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
|
||||
|
||||
- fix issue357 - special case "-k" expressions to allow for
|
||||
filtering with simple strings that are not valid python expressions.
|
||||
Examples: "-k 1.3" matches all tests parametrized with 1.3.
|
||||
"-k None" filters all tests that have "None" in their name
|
||||
and conversely "-k 'not None'".
|
||||
Previously these examples would raise syntax errors.
|
||||
|
||||
- fix issue384 by removing the trial support code
|
||||
since the unittest compat enhancements allow
|
||||
trial to handle it on its own
|
||||
|
||||
- don't hide an ImportError when importing a plugin produces one.
|
||||
fixes issue375.
|
||||
|
||||
- fix issue275 - allow usefixtures and autouse fixtures
|
||||
for running doctest text files.
|
||||
|
||||
- fix issue380 by making --resultlog only rely on longrepr instead
|
||||
of the "reprcrash" attribute which only exists sometimes.
|
||||
|
||||
- address issue122: allow @pytest.fixture(params=iterator) by exploding
|
||||
into a list early on.
|
||||
|
||||
- fix pexpect-3.0 compatibility for pytest's own tests.
|
||||
(fixes issue386)
|
||||
|
||||
- allow nested parametrize-value markers, thanks James Lan for the PR.
|
||||
|
||||
- fix unicode handling with new monkeypatch.setattr(import_path, value)
|
||||
API. Thanks Rob Dennis. Fixes issue371.
|
||||
|
||||
- fix unicode handling with junitxml, fixes issue368.
|
||||
|
||||
- In assertion rewriting mode on Python 2, fix the detection of coding
|
||||
cookies. See issue #330.
|
||||
|
||||
- make "--runxfail" turn imperative pytest.xfail calls into no ops
|
||||
(it already did neutralize pytest.mark.xfail markers)
|
||||
|
||||
- refine pytest / pkg_resources interactions: The AssertionRewritingHook
|
||||
PEP302 compliant loader now registers itself with setuptools/pkg_resources
|
||||
properly so that the pkg_resources.resource_stream method works properly.
|
||||
Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
|
||||
|
||||
- pytestconfig fixture is now session-scoped as it is the same object during the
|
||||
whole test run. Fixes issue370.
|
||||
|
||||
- avoid one surprising case of marker malfunction/confusion::
|
||||
|
||||
@pytest.mark.some(lambda arg: ...)
|
||||
def test_function():
|
||||
|
||||
would not work correctly because pytest assumes @pytest.mark.some
|
||||
gets a function to be decorated already. We now at least detect if this
|
||||
arg is an lambda and thus the example will work. Thanks Alex Gaynor
|
||||
for bringing it up.
|
||||
|
||||
- xfail a test on pypy that checks wrong encoding/ascii (pypy does
|
||||
not error out). fixes issue385.
|
||||
|
||||
- internally make varnames() deal with classes's __init__,
|
||||
although it's not needed by pytest itself atm. Also
|
||||
fix caching. Fixes issue376.
|
||||
|
||||
- fix issue221 - handle importing of namespace-package with no
|
||||
__init__.py properly.
|
||||
|
||||
- refactor internal FixtureRequest handling to avoid monkeypatching.
|
||||
One of the positive user-facing effects is that the "request" object
|
||||
can now be used in closures.
|
||||
|
||||
- fixed version comparison in pytest.importskip(modname, minverstring)
|
||||
|
||||
- fix issue377 by clarifying in the nose-compat docs that pytest
|
||||
does not duplicate the unittest-API into the "plain" namespace.
|
||||
|
||||
- fix verbose reporting for @mock'd test functions
|
||||
|
||||
v2.4.2
|
||||
-----------------------------------
|
||||
|
||||
- on Windows require colorama and a newer py lib so that py.io.TerminalWriter()
|
||||
@@ -24,19 +314,19 @@ Changes between 2.4.1 and 2.4.2
|
||||
|
||||
- remove attempt to "dup" stdout at startup as it's icky.
|
||||
the normal capturing should catch enough possibilities
|
||||
of tests messing up standard FDs.
|
||||
of tests messing up standard FDs.
|
||||
|
||||
- add pluginmanager.do_configure(config) as a link to
|
||||
- add pluginmanager.do_configure(config) as a link to
|
||||
config.do_configure() for plugin-compatibility
|
||||
|
||||
Changes between 2.4.0 and 2.4.1
|
||||
v2.4.1
|
||||
-----------------------------------
|
||||
|
||||
- When using parser.addoption() unicode arguments to the
|
||||
"type" keyword should also be converted to the respective types.
|
||||
thanks Floris Bruynooghe, @dnozay. (fixes issue360 and issue362)
|
||||
|
||||
- fix dotted filename completion when using argcomplete
|
||||
- fix dotted filename completion when using argcomplete
|
||||
thanks Anthon van der Neuth. (fixes issue361)
|
||||
|
||||
- fix regression when a 1-tuple ("arg",) is used for specifying
|
||||
@@ -45,7 +335,7 @@ Changes between 2.4.0 and 2.4.1
|
||||
|
||||
- merge doc typo fixes, thanks Andy Dirnberger
|
||||
|
||||
Changes between 2.3.5 and 2.4
|
||||
v2.4
|
||||
-----------------------------------
|
||||
|
||||
known incompatibilities:
|
||||
@@ -92,12 +382,12 @@ new features:
|
||||
- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
|
||||
"-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
|
||||
|
||||
- fix issue181: --pdb now also works on collect errors (and
|
||||
on internal errors) . This was implemented by a slight internal
|
||||
refactoring and the introduction of a new hook
|
||||
- fix issue181: --pdb now also works on collect errors (and
|
||||
on internal errors) . This was implemented by a slight internal
|
||||
refactoring and the introduction of a new hook
|
||||
``pytest_exception_interact`` hook (see next item).
|
||||
|
||||
- fix issue341: introduce new experimental hook for IDEs/terminals to
|
||||
- fix issue341: introduce new experimental hook for IDEs/terminals to
|
||||
intercept debugging: ``pytest_exception_interact(node, call, report)``.
|
||||
|
||||
- new monkeypatch.setattr() variant to provide a shorter
|
||||
@@ -115,7 +405,7 @@ new features:
|
||||
phase of a node.
|
||||
|
||||
- simplify pytest.mark.parametrize() signature: allow to pass a
|
||||
CSV-separated string to specify argnames. For example:
|
||||
CSV-separated string to specify argnames. For example:
|
||||
``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
|
||||
works as well as the previous:
|
||||
``pytest.mark.parametrize(("input", "expected"), ...)``.
|
||||
@@ -140,10 +430,10 @@ new features:
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- fix issue358 - capturing options are now parsed more properly
|
||||
- fix issue358 - capturing options are now parsed more properly
|
||||
by using a new parser.parse_known_args method.
|
||||
|
||||
- pytest now uses argparse instead of optparse (thanks Anthon) which
|
||||
- pytest now uses argparse instead of optparse (thanks Anthon) which
|
||||
means that "argparse" is added as a dependency if installing into python2.6
|
||||
environments or below.
|
||||
|
||||
@@ -184,7 +474,7 @@ Bug fixes:
|
||||
- fix issue323 - sorting of many module-scoped arg parametrizations
|
||||
|
||||
- make sessionfinish hooks execute with the same cwd-context as at
|
||||
session start (helps fix plugin behaviour which write output files
|
||||
session start (helps fix plugin behaviour which write output files
|
||||
with relative path such as pytest-cov)
|
||||
|
||||
- fix issue316 - properly reference collection hooks in docs
|
||||
@@ -192,7 +482,7 @@ Bug fixes:
|
||||
- fix issue 306 - cleanup of -k/-m options to only match markers/test
|
||||
names/keywords respectively. Thanks Wouter van Ackooy.
|
||||
|
||||
- improved doctest counting for doctests in python modules --
|
||||
- improved doctest counting for doctests in python modules --
|
||||
files without any doctest items will not show up anymore
|
||||
and doctest examples are counted as separate test items.
|
||||
thanks Danilo Bellini.
|
||||
@@ -202,7 +492,7 @@ Bug fixes:
|
||||
mode. Thanks Jason R. Coombs.
|
||||
|
||||
- fix junitxml generation when test output contains control characters,
|
||||
addressing issue267, thanks Jaap Broekhuizen
|
||||
addressing issue267, thanks Jaap Broekhuizen
|
||||
|
||||
- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
|
||||
|
||||
@@ -211,10 +501,10 @@ Bug fixes:
|
||||
- better parametrize error messages, thanks Brianna Laugher
|
||||
|
||||
- pytest_terminal_summary(terminalreporter) hooks can now use
|
||||
".section(title)" and ".line(msg)" methods to print extra
|
||||
".section(title)" and ".line(msg)" methods to print extra
|
||||
information at the end of a test run.
|
||||
|
||||
Changes between 2.3.4 and 2.3.5
|
||||
v2.3.5
|
||||
-----------------------------------
|
||||
|
||||
- fix issue169: respect --tb=style with setup/teardown errors as well.
|
||||
@@ -265,7 +555,7 @@ Changes between 2.3.4 and 2.3.5
|
||||
- fix bug where using capsys with pytest.set_trace() in a test
|
||||
function would break when looking at capsys.readouterr()
|
||||
|
||||
- allow to specify prefixes starting with "_" when
|
||||
- allow to specify prefixes starting with "_" when
|
||||
customizing python_functions test discovery. (thanks Graham Horler)
|
||||
|
||||
- improve PYTEST_DEBUG tracing output by puting
|
||||
@@ -279,10 +569,10 @@ Changes between 2.3.4 and 2.3.5
|
||||
|
||||
- fix issue266 - accept unicode in MarkEvaluator expressions
|
||||
|
||||
Changes between 2.3.3 and 2.3.4
|
||||
v2.3.4
|
||||
-----------------------------------
|
||||
|
||||
- yielded test functions will now have autouse-fixtures active but
|
||||
- yielded test functions will now have autouse-fixtures active but
|
||||
cannot accept fixtures as funcargs - it's anyway recommended to
|
||||
rather use the post-2.0 parametrize features instead of yield, see:
|
||||
http://pytest.org/latest/example/parametrize.html
|
||||
@@ -297,9 +587,9 @@ Changes between 2.3.3 and 2.3.4
|
||||
can write: -k "name1 or name2" etc. This is a slight incompatibility
|
||||
if you used special syntax like "TestClass.test_method" which you now
|
||||
need to write as -k "TestClass and test_method" to match a certain
|
||||
method in a certain test class.
|
||||
method in a certain test class.
|
||||
|
||||
Changes between 2.3.2 and 2.3.3
|
||||
v2.3.3
|
||||
-----------------------------------
|
||||
|
||||
- fix issue214 - parse modules that contain special objects like e. g.
|
||||
@@ -331,10 +621,10 @@ Changes between 2.3.2 and 2.3.3
|
||||
- fix issue127 - improve documentation for pytest_addoption() and
|
||||
add a ``config.getoption(name)`` helper function for consistency.
|
||||
|
||||
Changes between 2.3.1 and 2.3.2
|
||||
v2.3.2
|
||||
-----------------------------------
|
||||
|
||||
- fix issue208 and fix issue29 use new py version to avoid long pauses
|
||||
- fix issue208 and fix issue29 use new py version to avoid long pauses
|
||||
when printing tracebacks in long modules
|
||||
|
||||
- fix issue205 - conftests in subdirs customizing
|
||||
@@ -364,7 +654,7 @@ Changes between 2.3.1 and 2.3.2
|
||||
- add tox.ini to pytest distribution so that ignore-dirs and others config
|
||||
bits are properly distributed for maintainers who run pytest-own tests
|
||||
|
||||
Changes between 2.3.0 and 2.3.1
|
||||
v2.3.1
|
||||
-----------------------------------
|
||||
|
||||
- fix issue202 - fix regression: using "self" from fixture functions now
|
||||
@@ -377,7 +667,7 @@ Changes between 2.3.0 and 2.3.1
|
||||
- link to web pages from --markers output which provides help for
|
||||
pytest.mark.* usage.
|
||||
|
||||
Changes between 2.2.4 and 2.3.0
|
||||
v2.3.0
|
||||
-----------------------------------
|
||||
|
||||
- fix issue202 - better automatic names for parametrized test functions
|
||||
@@ -418,7 +708,7 @@ Changes between 2.2.4 and 2.3.0
|
||||
- pluginmanager.register(...) now raises ValueError if the
|
||||
plugin has been already registered or the name is taken
|
||||
|
||||
- fix issue159: improve http://pytest.org/latest/faq.html
|
||||
- fix issue159: improve http://pytest.org/latest/faq.html
|
||||
especially with respect to the "magic" history, also mention
|
||||
pytest-django, trial and unittest integration.
|
||||
|
||||
@@ -449,14 +739,14 @@ Changes between 2.2.4 and 2.3.0
|
||||
you can use startdir.bestrelpath(yourpath) to show
|
||||
nice relative path
|
||||
|
||||
- allow plugins to implement both pytest_report_header and
|
||||
- allow plugins to implement both pytest_report_header and
|
||||
pytest_sessionstart (sessionstart is invoked first).
|
||||
|
||||
- don't show deselected reason line if there is none
|
||||
|
||||
- py.test -vv will show all of assert comparisations instead of truncating
|
||||
|
||||
Changes between 2.2.3 and 2.2.4
|
||||
v2.2.4
|
||||
-----------------------------------
|
||||
|
||||
- fix error message for rewritten assertions involving the % operator
|
||||
@@ -473,17 +763,17 @@ Changes between 2.2.3 and 2.2.4
|
||||
- fix issue #144: better mangle test ids to junitxml classnames
|
||||
- upgrade distribute_setup.py to 0.6.27
|
||||
|
||||
Changes between 2.2.2 and 2.2.3
|
||||
v2.2.3
|
||||
----------------------------------------
|
||||
|
||||
- fix uploaded package to only include neccesary files
|
||||
|
||||
Changes between 2.2.1 and 2.2.2
|
||||
v2.2.2
|
||||
----------------------------------------
|
||||
|
||||
- fix issue101: wrong args to unittest.TestCase test function now
|
||||
produce better output
|
||||
- fix issue102: report more useful errors and hints for when a
|
||||
- fix issue102: report more useful errors and hints for when a
|
||||
test directory was renamed and some pyc/__pycache__ remain
|
||||
- fix issue106: allow parametrize to be applied multiple times
|
||||
e.g. from module, class and at function level.
|
||||
@@ -498,11 +788,11 @@ Changes between 2.2.1 and 2.2.2
|
||||
- allow adding of attributes to test reports such that it also works
|
||||
with distributed testing (no upgrade of pytest-xdist needed)
|
||||
|
||||
Changes between 2.2.0 and 2.2.1
|
||||
v2.2.1
|
||||
----------------------------------------
|
||||
|
||||
- fix issue99 (in pytest and py) internallerrors with resultlog now
|
||||
produce better output - fixed by normalizing pytest_internalerror
|
||||
produce better output - fixed by normalizing pytest_internalerror
|
||||
input arguments.
|
||||
- fix issue97 / traceback issues (in pytest and py) improve traceback output
|
||||
in conjunction with jinja2 and cython which hack tracebacks
|
||||
@@ -510,25 +800,25 @@ Changes between 2.2.0 and 2.2.1
|
||||
the final test in a test node will now run its teardown directly
|
||||
instead of waiting for the end of the session. Thanks Dave Hunt for
|
||||
the good reporting and feedback. The pytest_runtest_protocol as well
|
||||
as the pytest_runtest_teardown hooks now have "nextitem" available
|
||||
as the pytest_runtest_teardown hooks now have "nextitem" available
|
||||
which will be None indicating the end of the test run.
|
||||
- fix collection crash due to unknown-source collected items, thanks
|
||||
to Ralf Schmitt (fixed by depending on a more recent pylib)
|
||||
|
||||
Changes between 2.1.3 and 2.2.0
|
||||
v2.2.0
|
||||
----------------------------------------
|
||||
|
||||
- fix issue90: introduce eager tearing down of test items so that
|
||||
teardown function are called earlier.
|
||||
- add an all-powerful metafunc.parametrize function which allows to
|
||||
- add an all-powerful metafunc.parametrize function which allows to
|
||||
parametrize test function arguments in multiple steps and therefore
|
||||
from indepdenent plugins and palces.
|
||||
from indepdenent plugins and palces.
|
||||
- add a @pytest.mark.parametrize helper which allows to easily
|
||||
call a test function with different argument values
|
||||
- Add examples to the "parametrize" example page, including a quick port
|
||||
- Add examples to the "parametrize" example page, including a quick port
|
||||
of Test scenarios and the new parametrize function and decorator.
|
||||
- introduce registration for "pytest.mark.*" helpers via ini-files
|
||||
or through plugin hooks. Also introduce a "--strict" option which
|
||||
or through plugin hooks. Also introduce a "--strict" option which
|
||||
will treat unregistered markers as errors
|
||||
allowing to avoid typos and maintain a well described set of markers
|
||||
for your test suite. See exaples at http://pytest.org/latest/mark.html
|
||||
@@ -537,12 +827,12 @@ Changes between 2.1.3 and 2.2.0
|
||||
(this is a stricter and more predictable version of '-k' in that "-m"
|
||||
only matches complete markers and has more obvious rules for and/or
|
||||
semantics.
|
||||
- new feature to help optimizing the speed of your tests:
|
||||
--durations=N option for displaying N slowest test calls
|
||||
- new feature to help optimizing the speed of your tests:
|
||||
--durations=N option for displaying N slowest test calls
|
||||
and setup/teardown methods.
|
||||
- fix issue87: --pastebin now works with python3
|
||||
- fix issue89: --pdb with unexpected exceptions in doctest work more sensibly
|
||||
- fix and cleanup pytest's own test suite to not leak FDs
|
||||
- fix and cleanup pytest's own test suite to not leak FDs
|
||||
- fix issue83: link to generated funcarg list
|
||||
- fix issue74: pyarg module names are now checked against imp.find_module false positives
|
||||
- fix compatibility with twisted/trial-11.1.0 use cases
|
||||
@@ -550,7 +840,7 @@ Changes between 2.1.3 and 2.2.0
|
||||
- simplify junitxml output code by relying on py.xml
|
||||
- add support for skip properties on unittest classes and functions
|
||||
|
||||
Changes between 2.1.2 and 2.1.3
|
||||
v2.1.3
|
||||
----------------------------------------
|
||||
|
||||
- fix issue79: assertion rewriting failed on some comparisons in boolops
|
||||
@@ -559,7 +849,7 @@ Changes between 2.1.2 and 2.1.3
|
||||
- fix issue75 / skipping test failure on jython
|
||||
- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests
|
||||
|
||||
Changes between 2.1.1 and 2.1.2
|
||||
v2.1.2
|
||||
----------------------------------------
|
||||
|
||||
- fix assertion rewriting on files with windows newlines on some Python versions
|
||||
@@ -569,7 +859,7 @@ Changes between 2.1.1 and 2.1.2
|
||||
- fix issue66: use different assertion rewriting caches when the -O option is passed
|
||||
- don't try assertion rewriting on Jython, use reinterp
|
||||
|
||||
Changes between 2.1.0 and 2.1.1
|
||||
v2.1.1
|
||||
----------------------------------------------
|
||||
|
||||
- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks
|
||||
@@ -582,7 +872,7 @@ Changes between 2.1.0 and 2.1.1
|
||||
- fix issue61: assertion rewriting on boolean operations with 3 or more operands
|
||||
- you can now build a man page with "cd doc ; make man"
|
||||
|
||||
Changes between 2.0.3 and 2.1.0.DEV
|
||||
v2.1.0
|
||||
----------------------------------------------
|
||||
|
||||
- fix issue53 call nosestyle setup functions with correct ordering
|
||||
@@ -602,7 +892,7 @@ Changes between 2.0.3 and 2.1.0.DEV
|
||||
- report KeyboardInterrupt even if interrupted during session startup
|
||||
- fix issue 35 - provide PDF doc version and download link from index page
|
||||
|
||||
Changes between 2.0.2 and 2.0.3
|
||||
v2.0.3
|
||||
----------------------------------------------
|
||||
|
||||
- fix issue38: nicer tracebacks on calls to hooks, particularly early
|
||||
@@ -622,7 +912,7 @@ Changes between 2.0.2 and 2.0.3
|
||||
|
||||
- fix issue37: avoid invalid characters in junitxml's output
|
||||
|
||||
Changes between 2.0.1 and 2.0.2
|
||||
v2.0.2
|
||||
----------------------------------------------
|
||||
|
||||
- tackle issue32 - speed up test runs of very quick test functions
|
||||
@@ -634,17 +924,17 @@ Changes between 2.0.1 and 2.0.2
|
||||
|
||||
Also you can now access module globals from xfail/skipif
|
||||
expressions so that this for example works now::
|
||||
|
||||
|
||||
import pytest
|
||||
import mymodule
|
||||
@pytest.mark.skipif("mymodule.__version__[0] == "1")
|
||||
def test_function():
|
||||
pass
|
||||
|
||||
This will not run the test function if the module's version string
|
||||
This will not run the test function if the module's version string
|
||||
does not start with a "1". Note that specifying a string instead
|
||||
of a boolean expressions allows py.test to report meaningful information
|
||||
when summarizing a test run as to what conditions lead to skipping
|
||||
of a boolean expressions allows py.test to report meaningful information
|
||||
when summarizing a test run as to what conditions lead to skipping
|
||||
(or xfail-ing) tests.
|
||||
|
||||
- fix issue28 - setup_method and pytest_generate_tests work together
|
||||
@@ -667,14 +957,14 @@ Changes between 2.0.1 and 2.0.2
|
||||
- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
|
||||
thanks to Laura Creighton who also revieved parts of the documentation.
|
||||
|
||||
- fix slighly wrong output of verbose progress reporting for classes
|
||||
- fix slighly wrong output of verbose progress reporting for classes
|
||||
(thanks Amaury)
|
||||
|
||||
- more precise (avoiding of) deprecation warnings for node.Class|Function accesses
|
||||
|
||||
- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
|
||||
|
||||
Changes between 2.0.0 and 2.0.1
|
||||
v2.0.1
|
||||
----------------------------------------------
|
||||
|
||||
- refine and unify initial capturing so that it works nicely
|
||||
@@ -683,7 +973,7 @@ Changes between 2.0.0 and 2.0.1
|
||||
- allow to omit "()" in test ids to allow for uniform test ids
|
||||
as produced by Alfredo's nice pytest.vim plugin.
|
||||
- fix issue12 - show plugin versions with "--version" and
|
||||
"--traceconfig" and also document how to add extra information
|
||||
"--traceconfig" and also document how to add extra information
|
||||
to reporting test header
|
||||
- fix issue17 (import-* reporting issue on python3) by
|
||||
requiring py>1.4.0 (1.4.1 is going to include it)
|
||||
@@ -713,17 +1003,17 @@ Changes between 2.0.0 and 2.0.1
|
||||
- fix issue14: no logging errors at process exit
|
||||
- refinements to "collecting" output on non-ttys
|
||||
- refine internal plugin registration and --traceconfig output
|
||||
- introduce a mechanism to prevent/unregister plugins from the
|
||||
- introduce a mechanism to prevent/unregister plugins from the
|
||||
command line, see http://pytest.org/plugins.html#cmdunregister
|
||||
- activate resultlog plugin by default
|
||||
- fix regression wrt yielded tests which due to the
|
||||
collection-before-running semantics were not
|
||||
collection-before-running semantics were not
|
||||
setup as with pytest 1.3.4. Note, however, that
|
||||
the recommended and much cleaner way to do test
|
||||
the recommended and much cleaner way to do test
|
||||
parametraization remains the "pytest_generate_tests"
|
||||
mechanism, see the docs.
|
||||
|
||||
Changes between 1.3.4 and 2.0.0
|
||||
v2.0.0
|
||||
----------------------------------------------
|
||||
|
||||
- pytest-2.0 is now its own package and depends on pylib-2.0
|
||||
@@ -768,7 +1058,7 @@ Changes between 1.3.4 and 2.0.0
|
||||
- add ability to use "class" level for cached_setup helper
|
||||
- fix strangeness: mark.* objects are now immutable, create new instances
|
||||
|
||||
Changes between 1.3.3 and 1.3.4
|
||||
v1.3.4
|
||||
----------------------------------------------
|
||||
|
||||
- fix issue111: improve install documentation for windows
|
||||
@@ -777,7 +1067,7 @@ Changes between 1.3.3 and 1.3.4
|
||||
- fix issue115: unify internal exception passthrough/catching/GeneratorExit
|
||||
- fix issue118: new --tb=native for presenting cpython-standard exceptions
|
||||
|
||||
Changes between 1.3.2 and 1.3.3
|
||||
v1.3.3
|
||||
----------------------------------------------
|
||||
|
||||
- fix issue113: assertion representation problem with triple-quoted strings
|
||||
@@ -792,7 +1082,7 @@ Changes between 1.3.2 and 1.3.3
|
||||
(thanks Armin Ronacher for reporting)
|
||||
- remove trailing whitespace in all py/text distribution files
|
||||
|
||||
Changes between 1.3.1 and 1.3.2
|
||||
v1.3.2
|
||||
----------------------------------------------
|
||||
|
||||
New features
|
||||
@@ -867,7 +1157,7 @@ Bug fixes / Maintenance
|
||||
- fix homedir detection on Windows
|
||||
- ship distribute_setup.py version 0.6.13
|
||||
|
||||
Changes between 1.3.0 and 1.3.1
|
||||
v1.3.1
|
||||
---------------------------------------------
|
||||
|
||||
New features
|
||||
@@ -939,7 +1229,7 @@ Fixes / Maintenance
|
||||
(and internally be more careful when presenting unexpected byte sequences)
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.3.0
|
||||
v1.3.0
|
||||
---------------------------------------------
|
||||
|
||||
- deprecate --report option in favour of a new shorter and easier to
|
||||
@@ -1004,7 +1294,7 @@ Changes between 1.2.1 and 1.3.0
|
||||
- added links to the new capturelog and coverage plugins
|
||||
|
||||
|
||||
Changes between 1.2.1 and 1.2.0
|
||||
v1.2.0
|
||||
---------------------------------------------
|
||||
|
||||
- refined usage and options for "py.cleanup"::
|
||||
@@ -1043,7 +1333,7 @@ Changes between 1.2.1 and 1.2.0
|
||||
|
||||
- fix plugin links
|
||||
|
||||
Changes between 1.2 and 1.1.1
|
||||
v1.1.1
|
||||
---------------------------------------------
|
||||
|
||||
- moved dist/looponfailing from py.test core into a new
|
||||
@@ -1127,7 +1417,7 @@ Changes between 1.2 and 1.1.1
|
||||
- fix docs, fix internal bin/ script generation
|
||||
|
||||
|
||||
Changes between 1.1.1 and 1.1.0
|
||||
v1.1.0
|
||||
---------------------------------------------
|
||||
|
||||
- introduce automatic plugin registration via 'pytest11'
|
||||
@@ -1146,7 +1436,7 @@ Changes between 1.1.1 and 1.1.0
|
||||
- try harder to have deprecation warnings for py.compat.* accesses
|
||||
report a correct location
|
||||
|
||||
Changes between 1.1.0 and 1.0.2
|
||||
v1.0.2
|
||||
---------------------------------------------
|
||||
|
||||
* adjust and improve docs
|
||||
@@ -1231,7 +1521,7 @@ Changes between 1.1.0 and 1.0.2
|
||||
|
||||
* simplified internal localpath implementation
|
||||
|
||||
Changes between 1.0.1 and 1.0.2
|
||||
v1.0.2
|
||||
-------------------------------------------
|
||||
|
||||
* fixing packaging issues, triggered by fedora redhat packaging,
|
||||
@@ -1239,7 +1529,7 @@ Changes between 1.0.1 and 1.0.2
|
||||
|
||||
* added a documentation link to the new django plugin.
|
||||
|
||||
Changes between 1.0.0 and 1.0.1
|
||||
v1.0.1
|
||||
-------------------------------------------
|
||||
|
||||
* added a 'pytest_nose' plugin which handles nose.SkipTest,
|
||||
@@ -1273,13 +1563,13 @@ Changes between 1.0.0 and 1.0.1
|
||||
* simplified multicall mechanism and plugin architecture,
|
||||
renamed some internal methods and argnames
|
||||
|
||||
Changes between 1.0.0b9 and 1.0.0
|
||||
v1.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
|
||||
v1.0.0b9
|
||||
-------------------------------------------
|
||||
|
||||
* cleanly handle and report final teardown of test setup
|
||||
@@ -1313,7 +1603,7 @@ Changes between 1.0.0b8 and 1.0.0b9
|
||||
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
|
||||
|
||||
|
||||
Changes between 1.0.0b7 and 1.0.0b8
|
||||
v1.0.0b8
|
||||
-------------------------------------------
|
||||
|
||||
* pytest_unittest-plugin is now enabled by default
|
||||
@@ -1342,7 +1632,7 @@ Changes between 1.0.0b7 and 1.0.0b8
|
||||
* tweaked doctest output for docstrings in py modules,
|
||||
thanks Radomir.
|
||||
|
||||
Changes between 1.0.0b3 and 1.0.0b7
|
||||
v1.0.0b7
|
||||
-------------------------------------------
|
||||
|
||||
* renamed py.test.xfail back to py.test.mark.xfail to avoid
|
||||
@@ -1367,7 +1657,7 @@ Changes between 1.0.0b3 and 1.0.0b7
|
||||
|
||||
* make __name__ == "__channelexec__" for remote_exec code
|
||||
|
||||
Changes between 1.0.0b1 and 1.0.0b3
|
||||
v1.0.0b3
|
||||
-------------------------------------------
|
||||
|
||||
* plugin classes are removed: one now defines
|
||||
@@ -1384,7 +1674,7 @@ Changes between 1.0.0b1 and 1.0.0b3
|
||||
well with function arguments.
|
||||
|
||||
|
||||
Changes between 0.9.2 and 1.0.0b1
|
||||
v1.0.0b1
|
||||
-------------------------------------------
|
||||
|
||||
* introduced new "funcarg" setup method,
|
||||
@@ -1408,7 +1698,7 @@ Changes between 0.9.2 and 1.0.0b1
|
||||
|
||||
XXX lots of things missing here XXX
|
||||
|
||||
Changes between 0.9.1 and 0.9.2
|
||||
v0.9.2
|
||||
-------------------------------------------
|
||||
|
||||
* refined installation and metadata, created new setup.py,
|
||||
@@ -1441,10 +1731,10 @@ Changes between 0.9.1 and 0.9.2
|
||||
|
||||
* there now is a py.__version__ attribute
|
||||
|
||||
Changes between 0.9.0 and 0.9.1
|
||||
v0.9.1
|
||||
-------------------------------------------
|
||||
|
||||
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
|
||||
This is a fairly complete list of v0.9.1, which can
|
||||
serve as a reference for developers.
|
||||
|
||||
* allowing + signs in py.path.svn urls [39106]
|
||||
|
||||
171
CONTRIBUTING.rst
Normal file
171
CONTRIBUTING.rst
Normal file
@@ -0,0 +1,171 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
Contributions are highly welcomed and appreciated. Every little help counts,
|
||||
so do not hesitate!
|
||||
|
||||
|
||||
Types of contributions
|
||||
======================
|
||||
|
||||
Report bugs
|
||||
-----------
|
||||
|
||||
Report bugs at https://bitbucket.org/hpk42/pytest/issues.
|
||||
|
||||
If you are reporting a bug, please include:
|
||||
|
||||
* Your operating system name and version.
|
||||
* Any details about your local setup that might be helpful in troubleshooting,
|
||||
specifically Python interpreter version,
|
||||
installed libraries and pytest version.
|
||||
* Detailed steps to reproduce the bug.
|
||||
|
||||
Submit feedback for developers
|
||||
------------------------------
|
||||
|
||||
Do you like pytest? Share some love on Twitter or in your blog posts!
|
||||
|
||||
We'd also like to hear about your propositions and suggestions. Feel free to
|
||||
`submit them as issues <https://bitbucket.org/hpk42/pytest/issues>`__ and:
|
||||
|
||||
* Set the "kind" to "enhancement" or "proposal" so that we can quickly find
|
||||
about them.
|
||||
* Explain in detail how they should work.
|
||||
* Keep the scope as narrow as possible. This will make it easier to implement.
|
||||
* If you have required skills and/or knowledge, we are very happy for
|
||||
:ref:`pull requests <pull-requests>`.
|
||||
|
||||
|
||||
Fix bugs
|
||||
--------
|
||||
|
||||
Look through the BitBucket issues for bugs. Here is sample filter you can use:
|
||||
https://bitbucket.org/hpk42/pytest/issues?status=new&status=open&kind=bug
|
||||
|
||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
|
||||
|
||||
Implement features
|
||||
------------------
|
||||
|
||||
Look through the BitBucket issues for enhancements. Here is sample filter you
|
||||
can use:
|
||||
https://bitbucket.org/hpk42/pytest/issues?status=new&status=open&kind=enhancement
|
||||
|
||||
:ref:`Talk <contact>` to developers to find out how you can implement specific
|
||||
features.
|
||||
|
||||
Write documentation
|
||||
-------------------
|
||||
|
||||
pytest could always use more documentation. What exactly is needed?
|
||||
|
||||
* More complementary documentation. Have you perhaps found something unclear?
|
||||
* Documentation translations. We currently have English and Japanese versions.
|
||||
* Docstrings. There's never too much of them.
|
||||
* Blog posts, articles and such -- they're all very appreciated.
|
||||
|
||||
.. _pull-requests:
|
||||
|
||||
Preparing Pull Requests on Bitbucket
|
||||
=====================================
|
||||
|
||||
.. note::
|
||||
What is a "pull request"? It informs project's core developers about the
|
||||
changes you want to review and merge. Pull requests are stored on
|
||||
`BitBucket servers <https://bitbucket.org/hpk42/pytest/pull-requests>`__.
|
||||
Once you send pull request, we can discuss it's potential modifications and
|
||||
even add more commits to it later on.
|
||||
|
||||
The primary development platform for pytest is BitBucket. You can find all
|
||||
the issues there and submit your pull requests.
|
||||
|
||||
1. Fork the
|
||||
`pytest BitBucket repository <https://bitbucket.org/hpk42/pytest>`__. It's
|
||||
fine to use ``pytest`` as your fork repository name because it will live
|
||||
under your user.
|
||||
|
||||
.. _virtualenvactivate:
|
||||
|
||||
2. Create and activate a fork-specific virtualenv
|
||||
(http://www.virtualenv.org/en/latest/)::
|
||||
|
||||
$ virtualenv pytest-venv
|
||||
$ source pytest-venv/bin/activate
|
||||
|
||||
.. _checkout:
|
||||
|
||||
3. Clone your fork locally using `Mercurial <http://mercurial.selenic.com/>`_
|
||||
(``hg``) and create a branch::
|
||||
|
||||
$ hg clone ssh://hg@bitbucket.org/YOUR_BITBUCKET_USERNAME/pytest
|
||||
$ cd pytest
|
||||
$ hg branch your-branch-name
|
||||
|
||||
If you need some help with Mercurial, follow this quick start
|
||||
guide: http://mercurial.selenic.com/wiki/QuickStart
|
||||
|
||||
.. _testing-pytest:
|
||||
|
||||
4. You can now edit your local working copy. To test you need to
|
||||
install the "tox" tool into your virtualenv::
|
||||
|
||||
$ pip install tox
|
||||
|
||||
You need to have Python 2.7 and 3.3 available in your system. Now
|
||||
running tests is as simple as issuing this command::
|
||||
|
||||
$ python runtox.py -e py27,py33,flakes
|
||||
|
||||
This command will run tests via the "tox" tool against Python 2.7 and 3.3
|
||||
and also perform "flakes" coding-style checks. ``runtox.py`` is
|
||||
a thin wrapper around ``tox`` which installs from a development package
|
||||
index where newer (not yet released to pypi) versions of dependencies
|
||||
(especially ``py``) might be present.
|
||||
|
||||
To run tests on py27 and pass options (e.g. enter pdb on failure)
|
||||
to pytest you can do::
|
||||
|
||||
$ python runtox.py -e py27 -- --pdb
|
||||
|
||||
or to only run tests in a particular test module on py33::
|
||||
|
||||
$ python runtox.py -e py33 -- testing/test_config.py
|
||||
|
||||
5. Commit and push once your tests pass and you are happy with your change(s)::
|
||||
|
||||
$ hg commit -m"<commit message>"
|
||||
$ hg push -b .
|
||||
|
||||
6. Finally, submit a pull request through the BitBucket website:
|
||||
|
||||
.. image:: img/pullrequest.png
|
||||
:width: 700px
|
||||
:align: center
|
||||
|
||||
::
|
||||
|
||||
source: YOUR_BITBUCKET_USERNAME/pytest
|
||||
branch: your-branch-name
|
||||
|
||||
target: hpk42/pytest
|
||||
branch: default
|
||||
|
||||
.. _contribution-using-git:
|
||||
|
||||
What about git (and so GitHub)?
|
||||
-------------------------------
|
||||
|
||||
There used to be the pytest GitHub mirror. It was removed in favor of the
|
||||
Mercurial one, to remove confusion of people not knowing where it's better to
|
||||
put their issues and pull requests. Also it wasn't easily possible to automate
|
||||
the mirroring process.
|
||||
|
||||
However, it's still possible to use git to contribute to pytest using tools
|
||||
like `gitifyhg <https://github.com/buchuki/gitifyhg>`_ which allows you to
|
||||
clone and work with Mercurial repo still using git.
|
||||
|
||||
.. warning::
|
||||
Remember that git is **not** a default version control system for pytest and
|
||||
you need to be careful using it.
|
||||
49
ISSUES.txt
49
ISSUES.txt
@@ -79,15 +79,6 @@ style tests, i.e. it leverages existing test suites without needing
|
||||
to rewrite them. Together with the previously mentioned setup_test()
|
||||
maybe the setupfunc could be ommitted?
|
||||
|
||||
checks / deprecations for next release
|
||||
---------------------------------------------------------------
|
||||
tags: bug 2.4 core xdist
|
||||
|
||||
* check oejskit plugin compatibility
|
||||
* move pytest_nose out of pylib because it implicitely extends
|
||||
the protocol now - setup/teardown is called at module level.
|
||||
consider making calling of setup/teardown configurable
|
||||
|
||||
optimizations
|
||||
---------------------------------------------------------------
|
||||
tags: 2.4 core
|
||||
@@ -122,8 +113,8 @@ customize test function collection
|
||||
-------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
- introduce py.test.mark.nocollect for not considering a function for
|
||||
test collection at all. maybe also introduce a py.test.mark.test to
|
||||
- introduce pytest.mark.nocollect for not considering a function for
|
||||
test collection at all. maybe also introduce a pytest.mark.test to
|
||||
explicitely mark a function to become a tested one. Lookup JUnit ways
|
||||
of tagging tests.
|
||||
|
||||
@@ -135,18 +126,18 @@ in addition to the imperative pytest.importorskip also introduce
|
||||
a pytest.mark.importorskip so that the test count is more correct.
|
||||
|
||||
|
||||
introduce py.test.mark.platform
|
||||
introduce pytest.mark.platform
|
||||
-------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
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)
|
||||
@pytest.mark.platform("python3")
|
||||
@pytest.mark.platform("not python3")
|
||||
@pytest.mark.platform("win32 and not python3")
|
||||
@pytest.mark.platform("darwin")
|
||||
@pytest.mark.platform("not (jython and win32)")
|
||||
@pytest.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
|
||||
@@ -181,8 +172,8 @@ tags: feature
|
||||
allow to name conftest.py files (in sub directories) that should
|
||||
be imported early, as to include command line options.
|
||||
|
||||
improve central py.test ini file
|
||||
----------------------------------
|
||||
improve central pytest ini file
|
||||
-------------------------------
|
||||
tags: feature
|
||||
|
||||
introduce more declarative configuration options:
|
||||
@@ -196,7 +187,7 @@ new documentation
|
||||
----------------------------------
|
||||
tags: feature
|
||||
|
||||
- logo py.test
|
||||
- logo pytest
|
||||
- examples for unittest or functional testing
|
||||
- resource management for functional testing
|
||||
- patterns: page object
|
||||
@@ -205,17 +196,17 @@ have imported module mismatch honour relative paths
|
||||
--------------------------------------------------------
|
||||
tags: bug
|
||||
|
||||
With 1.1.1 py.test fails at least on windows if an import
|
||||
With 1.1.1 pytest fails at least on windows if an import
|
||||
is relative and compared against an absolute conftest.py
|
||||
path. Normalize.
|
||||
|
||||
consider globals: py.test.ensuretemp and config
|
||||
consider globals: pytest.ensuretemp and config
|
||||
--------------------------------------------------------------
|
||||
tags: experimental-wish
|
||||
|
||||
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
|
||||
consider deprecating pytest.ensuretemp and pytest.config
|
||||
to further reduce pytest globality. Also consider
|
||||
having pytest.config and ensuretemp coming from
|
||||
a plugin rather than being there from the start.
|
||||
|
||||
|
||||
@@ -223,7 +214,7 @@ consider pytest_addsyspath hook
|
||||
-----------------------------------------
|
||||
tags: wish
|
||||
|
||||
py.test could call a new pytest_addsyspath() in order to systematically
|
||||
pytest 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.
|
||||
|
||||
@@ -232,11 +223,11 @@ and pytest_configure.
|
||||
|
||||
|
||||
|
||||
deprecate global py.test.config usage
|
||||
deprecate global pytest.config usage
|
||||
----------------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
py.test.ensuretemp and py.test.config are probably the last
|
||||
pytest.ensuretemp and pytest.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
|
||||
|
||||
22
README.rst
22
README.rst
@@ -1,7 +1,13 @@
|
||||
|
||||
Documentation: http://pytest.org/latest/
|
||||
|
||||
Changelog: http://pytest.org/latest/changelog.html
|
||||
|
||||
The ``py.test`` testing tool makes it easy to write small tests, yet
|
||||
Issues: https://bitbucket.org/hpk42/pytest/issues?status=open
|
||||
|
||||
CI: https://drone.io/bitbucket.org/hpk42/pytest
|
||||
|
||||
The ``pytest`` testing tool makes it easy to write small tests, yet
|
||||
scales to support complex functional testing. It provides
|
||||
|
||||
- `auto-discovery
|
||||
@@ -10,17 +16,14 @@ scales to support complex functional testing. It provides
|
||||
- detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names)
|
||||
- `modular fixtures <http://pytest.org/latest/fixture.html>`_ for
|
||||
managing small or parametrized long-lived test resources.
|
||||
- multi-paradigm support: you can use ``py.test`` to run test suites based
|
||||
- multi-paradigm support: you can use ``pytest`` to run test suites based
|
||||
on `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
|
||||
`nose <http://pytest.org/latest/nose.html>`_
|
||||
- single-source compatibility to Python2.4 all the way up to Python3.3,
|
||||
PyPy-1.9 and Jython-2.5.1.
|
||||
- single-source compatibility to Python2.5 all the way up to Python3.4,
|
||||
PyPy-2.3 and Jython-2.5.1.
|
||||
|
||||
- many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_.
|
||||
|
||||
.. image:: https://secure.travis-ci.org/hpk42/pytest.png
|
||||
:target: http://travis-ci.org/hpk42/pytest
|
||||
|
||||
A simple example for a test::
|
||||
|
||||
# content of test_module.py
|
||||
@@ -38,11 +41,10 @@ and report bugs at:
|
||||
|
||||
http://bitbucket.org/hpk42/pytest/issues/
|
||||
|
||||
and checkout repos at:
|
||||
and checkout or fork repo at:
|
||||
|
||||
http://github.com/hpk42/pytest/ (mirror)
|
||||
http://bitbucket.org/hpk42/pytest/
|
||||
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2013
|
||||
Copyright Holger Krekel and others, 2004-2014
|
||||
Licensed under the MIT license.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#
|
||||
__version__ = '2.4.2'
|
||||
__version__ = '2.6.0'
|
||||
|
||||
@@ -6,20 +6,32 @@ import sys
|
||||
from _pytest.monkeypatch import monkeypatch
|
||||
from _pytest.assertion import util
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("debugconfig")
|
||||
group.addoption('--assert', action="store", dest="assertmode",
|
||||
group.addoption('--assert',
|
||||
action="store",
|
||||
dest="assertmode",
|
||||
choices=("rewrite", "reinterp", "plain",),
|
||||
default="rewrite", metavar="MODE",
|
||||
help="""control assertion debugging tools.
|
||||
'plain' performs no assertion debugging.
|
||||
'reinterp' reinterprets assert statements after they failed to provide assertion expression information.
|
||||
'rewrite' (the default) rewrites assert statements in test modules on import
|
||||
to provide assert expression information. """)
|
||||
group.addoption('--no-assert', action="store_true", default=False,
|
||||
dest="noassert", help="DEPRECATED equivalent to --assert=plain")
|
||||
group.addoption('--nomagic', '--no-magic', action="store_true",
|
||||
default=False, help="DEPRECATED equivalent to --assert=plain")
|
||||
default="rewrite",
|
||||
metavar="MODE",
|
||||
help="""control assertion debugging tools. 'plain'
|
||||
performs no assertion debugging. 'reinterp'
|
||||
reinterprets assert statements after they failed
|
||||
to provide assertion expression information.
|
||||
'rewrite' (the default) rewrites assert
|
||||
statements in test modules on import to
|
||||
provide assert expression information. """)
|
||||
group.addoption('--no-assert',
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="noassert",
|
||||
help="DEPRECATED equivalent to --assert=plain")
|
||||
group.addoption('--nomagic', '--no-magic',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="DEPRECATED equivalent to --assert=plain")
|
||||
|
||||
|
||||
class AssertionState:
|
||||
"""State for the assertion plugin."""
|
||||
@@ -28,41 +40,44 @@ class AssertionState:
|
||||
self.mode = mode
|
||||
self.trace = config.trace.root.get("assertion")
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
mode = config.getvalue("assertmode")
|
||||
if config.getvalue("noassert") or config.getvalue("nomagic"):
|
||||
mode = "plain"
|
||||
if mode == "rewrite":
|
||||
try:
|
||||
import ast
|
||||
import ast # noqa
|
||||
except ImportError:
|
||||
mode = "reinterp"
|
||||
else:
|
||||
# Both Jython and CPython 2.6.0 have AST bugs that make the
|
||||
# assertion rewriting hook malfunction.
|
||||
if (sys.platform.startswith('java') or
|
||||
sys.version_info[:3] == (2, 6, 0)):
|
||||
sys.version_info[:3] == (2, 6, 0)):
|
||||
mode = "reinterp"
|
||||
if mode != "plain":
|
||||
_load_modules(mode)
|
||||
m = monkeypatch()
|
||||
config._cleanup.append(m.undo)
|
||||
m.setattr(py.builtin.builtins, 'AssertionError',
|
||||
reinterpret.AssertionError)
|
||||
reinterpret.AssertionError) # noqa
|
||||
hook = None
|
||||
if mode == "rewrite":
|
||||
hook = rewrite.AssertionRewritingHook()
|
||||
hook = rewrite.AssertionRewritingHook() # noqa
|
||||
sys.meta_path.insert(0, hook)
|
||||
warn_about_missing_assertion(mode)
|
||||
config._assertstate = AssertionState(config, mode)
|
||||
config._assertstate.hook = hook
|
||||
config._assertstate.trace("configured with mode set to %r" % (mode,))
|
||||
|
||||
|
||||
def pytest_unconfigure(config):
|
||||
hook = config._assertstate.hook
|
||||
if hook is not None:
|
||||
if hook is not None and hook in sys.meta_path:
|
||||
sys.meta_path.remove(hook)
|
||||
|
||||
|
||||
def pytest_collection(session):
|
||||
# this hook is only called when test modules are collected
|
||||
# so for example not in the master process of pytest-xdist
|
||||
@@ -71,39 +86,62 @@ def pytest_collection(session):
|
||||
if hook is not None:
|
||||
hook.set_session(session)
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
"""Setup the pytest_assertrepr_compare hook
|
||||
|
||||
The newinterpret and rewrite modules will use util._reprcompare if
|
||||
it exists to use custom reporting via the
|
||||
pytest_assertrepr_compare hook. This sets up this custom
|
||||
comparison for the test.
|
||||
"""
|
||||
def callbinrepr(op, left, right):
|
||||
"""Call the pytest_assertrepr_compare hook and prepare the result
|
||||
|
||||
This uses the first result from the hook and then ensures the
|
||||
following:
|
||||
* Overly verbose explanations are dropped unles -vv was used.
|
||||
* Embedded newlines are escaped to help util.format_explanation()
|
||||
later.
|
||||
* If the rewrite mode is used embedded %-characters are replaced
|
||||
to protect later % formatting.
|
||||
|
||||
The result can be formatted by util.format_explanation() for
|
||||
pretty printing.
|
||||
"""
|
||||
hook_result = item.ihook.pytest_assertrepr_compare(
|
||||
config=item.config, op=op, left=left, right=right)
|
||||
|
||||
for new_expl in hook_result:
|
||||
if new_expl:
|
||||
# Don't include pageloads of data unless we are very verbose (-vv)
|
||||
if len(''.join(new_expl[1:])) > 80*8 and item.config.option.verbose < 2:
|
||||
new_expl[1:] = ['Detailed information truncated, use "-vv" to see']
|
||||
res = '\n~'.join(new_expl)
|
||||
if (sum(len(p) for p in new_expl[1:]) > 80*8
|
||||
and item.config.option.verbose < 2):
|
||||
new_expl[1:] = [py.builtin._totext(
|
||||
'Detailed information truncated, use "-vv" to show')]
|
||||
new_expl = [line.replace("\n", "\\n") for line in new_expl]
|
||||
res = py.builtin._totext("\n~").join(new_expl)
|
||||
if item.config.getvalue("assertmode") == "rewrite":
|
||||
# The result will be fed back a python % formatting
|
||||
# operation, which will fail if there are extraneous
|
||||
# '%'s in the string. Escape them here.
|
||||
res = res.replace("%", "%%")
|
||||
return res
|
||||
util._reprcompare = callbinrepr
|
||||
|
||||
|
||||
def pytest_runtest_teardown(item):
|
||||
util._reprcompare = None
|
||||
|
||||
|
||||
def pytest_sessionfinish(session):
|
||||
hook = session.config._assertstate.hook
|
||||
if hook is not None:
|
||||
hook.session = None
|
||||
|
||||
|
||||
def _load_modules(mode):
|
||||
"""Lazily import assertion related code."""
|
||||
global rewrite, reinterpret
|
||||
from _pytest.assertion import reinterpret
|
||||
from _pytest.assertion import reinterpret # noqa
|
||||
if mode == "rewrite":
|
||||
from _pytest.assertion import rewrite
|
||||
from _pytest.assertion import rewrite # noqa
|
||||
|
||||
|
||||
def warn_about_missing_assertion(mode):
|
||||
try:
|
||||
@@ -118,8 +156,10 @@ def warn_about_missing_assertion(mode):
|
||||
specifically = "failing tests may report as passing"
|
||||
|
||||
sys.stderr.write("WARNING: " + specifically +
|
||||
" because assert statements are not executed "
|
||||
"by the underlying Python interpreter "
|
||||
"(are you using python -O?)\n")
|
||||
" because assert statements are not executed "
|
||||
"by the underlying Python interpreter "
|
||||
"(are you using python -O?)\n")
|
||||
|
||||
|
||||
# Expose this plugin's implementation for the pytest_assertrepr_compare hook
|
||||
pytest_assertrepr_compare = util.assertrepr_compare
|
||||
|
||||
@@ -286,7 +286,19 @@ class DebugInterpreter(ast.NodeVisitor):
|
||||
source = "__exprinfo_expr.%s" % (attr.attr,)
|
||||
co = self._compile(source)
|
||||
try:
|
||||
result = self.frame.eval(co, __exprinfo_expr=source_result)
|
||||
try:
|
||||
result = self.frame.eval(co, __exprinfo_expr=source_result)
|
||||
except AttributeError:
|
||||
# Maybe the attribute name needs to be mangled?
|
||||
if not attr.attr.startswith("__") or attr.attr.endswith("__"):
|
||||
raise
|
||||
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
|
||||
co = self._compile(source)
|
||||
class_name = self.frame.eval(co, __exprinfo_expr=source_result)
|
||||
mangled_attr = "_" + class_name + attr.attr
|
||||
source = "__exprinfo_expr.%s" % (mangled_attr,)
|
||||
co = self._compile(source)
|
||||
result = self.frame.eval(co, __exprinfo_expr=source_result)
|
||||
except Exception:
|
||||
raise Failure(explanation)
|
||||
explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
|
||||
|
||||
@@ -355,7 +355,18 @@ class Getattr(Interpretable):
|
||||
expr.eval(frame)
|
||||
source = '__exprinfo_expr.%s' % self.attrname
|
||||
try:
|
||||
self.result = frame.eval(source, __exprinfo_expr=expr.result)
|
||||
try:
|
||||
self.result = frame.eval(source, __exprinfo_expr=expr.result)
|
||||
except AttributeError:
|
||||
# Maybe the attribute name needs to be mangled?
|
||||
if (not self.attrname.startswith("__") or
|
||||
self.attrname.endswith("__")):
|
||||
raise
|
||||
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
|
||||
class_name = frame.eval(source, __exprinfo_expr=expr.result)
|
||||
mangled_attr = "_" + class_name + self.attrname
|
||||
source = "__exprinfo_expr.%s" % (mangled_attr,)
|
||||
self.result = frame.eval(source, __exprinfo_expr=expr.result)
|
||||
except passthroughex:
|
||||
raise
|
||||
except:
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import sys
|
||||
import py
|
||||
from _pytest.assertion.util import BuiltinAssertionError
|
||||
u = py.builtin._totext
|
||||
|
||||
|
||||
class AssertionError(BuiltinAssertionError):
|
||||
def __init__(self, *args):
|
||||
BuiltinAssertionError.__init__(self, *args)
|
||||
if args:
|
||||
# on Python2.6 we get len(args)==2 for: assert 0, (x,y)
|
||||
# on Python2.7 and above we always get len(args) == 1
|
||||
# with args[0] being the (x,y) tuple.
|
||||
if len(args) > 1:
|
||||
toprint = args
|
||||
else:
|
||||
toprint = args[0]
|
||||
try:
|
||||
self.msg = str(args[0])
|
||||
except py.builtin._sysex:
|
||||
raise
|
||||
except:
|
||||
self.msg = "<[broken __repr__] %s at %0xd>" %(
|
||||
args[0].__class__, id(args[0]))
|
||||
self.msg = u(toprint)
|
||||
except Exception:
|
||||
self.msg = u(
|
||||
"<[broken __repr__] %s at %0xd>"
|
||||
% (toprint.__class__, id(toprint)))
|
||||
else:
|
||||
f = py.code.Frame(sys._getframe(1))
|
||||
try:
|
||||
@@ -37,10 +45,8 @@ class AssertionError(BuiltinAssertionError):
|
||||
|
||||
if sys.version_info > (3, 0):
|
||||
AssertionError.__module__ = "builtins"
|
||||
reinterpret_old = "old reinterpretation not available for py3"
|
||||
else:
|
||||
from _pytest.assertion.oldinterpret import interpret as reinterpret_old
|
||||
if sys.version_info >= (2, 6) or (sys.platform.startswith("java")):
|
||||
|
||||
if sys.version_info >= (2, 6) or sys.platform.startswith("java"):
|
||||
from _pytest.assertion.newinterpret import interpret as reinterpret
|
||||
else:
|
||||
reinterpret = reinterpret_old
|
||||
from _pytest.assertion.oldinterpret import interpret as reinterpret
|
||||
|
||||
@@ -15,7 +15,7 @@ import py
|
||||
from _pytest.assertion import util
|
||||
|
||||
|
||||
# py.test caches rewritten pycs in __pycache__.
|
||||
# pytest caches rewritten pycs in __pycache__.
|
||||
if hasattr(imp, "get_tag"):
|
||||
PYTEST_TAG = imp.get_tag() + "-PYTEST"
|
||||
else:
|
||||
@@ -41,6 +41,7 @@ class AssertionRewritingHook(object):
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
self.modules = {}
|
||||
self._register_with_pkg_resources()
|
||||
|
||||
def set_session(self, session):
|
||||
self.fnpats = session.config.getini("python_files")
|
||||
@@ -55,8 +56,12 @@ class AssertionRewritingHook(object):
|
||||
names = name.rsplit(".", 1)
|
||||
lastname = names[-1]
|
||||
pth = None
|
||||
if path is not None and len(path) == 1:
|
||||
pth = path[0]
|
||||
if path is not None:
|
||||
# Starting with Python 3.3, path is a _NamespacePath(), which
|
||||
# causes problems if not converted to list.
|
||||
path = list(path)
|
||||
if len(path) == 1:
|
||||
pth = path[0]
|
||||
if pth is None:
|
||||
try:
|
||||
fd, fn, desc = imp.find_module(lastname, path)
|
||||
@@ -97,7 +102,7 @@ class AssertionRewritingHook(object):
|
||||
# the most magical part of the process: load the source, rewrite the
|
||||
# asserts, and load the rewritten source. We also cache the rewritten
|
||||
# module code in a special pyc. We must be aware of the possibility of
|
||||
# concurrent py.test processes rewriting and loading pycs. To avoid
|
||||
# concurrent pytest processes rewriting and loading pycs. To avoid
|
||||
# tricky race conditions, we maintain the following invariant: The
|
||||
# cached pyc is always a complete, valid pyc. Operations on it must be
|
||||
# atomic. POSIX's atomic rename comes in handy.
|
||||
@@ -169,6 +174,24 @@ class AssertionRewritingHook(object):
|
||||
tp = desc[2]
|
||||
return tp == imp.PKG_DIRECTORY
|
||||
|
||||
@classmethod
|
||||
def _register_with_pkg_resources(cls):
|
||||
"""
|
||||
Ensure package resources can be loaded from this loader. May be called
|
||||
multiple times, as the operation is idempotent.
|
||||
"""
|
||||
try:
|
||||
import pkg_resources
|
||||
# access an attribute in case a deferred importer is present
|
||||
pkg_resources.__name__
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
# Since pytest tests are always located in the file system, the
|
||||
# DefaultProvider is appropriate.
|
||||
pkg_resources.register_loader_type(cls, pkg_resources.DefaultProvider)
|
||||
|
||||
|
||||
def _write_pyc(state, co, source_path, pyc):
|
||||
# Technically, we don't have to have the same pyc format as
|
||||
# (C)Python, since these "pycs" should never be seen by builtin
|
||||
@@ -196,7 +219,7 @@ def _write_pyc(state, co, source_path, pyc):
|
||||
RN = "\r\n".encode("utf-8")
|
||||
N = "\n".encode("utf-8")
|
||||
|
||||
cookie_re = re.compile("coding[:=]\s*[-\w.]+")
|
||||
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
|
||||
BOM_UTF8 = '\xef\xbb\xbf'
|
||||
|
||||
def _rewrite_test(state, fn):
|
||||
@@ -220,8 +243,8 @@ def _rewrite_test(state, fn):
|
||||
end1 = source.find("\n")
|
||||
end2 = source.find("\n", end1 + 1)
|
||||
if (not source.startswith(BOM_UTF8) and
|
||||
(not cookie_re.match(source[0:end1]) or
|
||||
not cookie_re.match(source[end1:end2]))):
|
||||
cookie_re.match(source[0:end1]) is None and
|
||||
cookie_re.match(source[end1 + 1:end2]) is None):
|
||||
if hasattr(state, "_indecode"):
|
||||
return None # encodings imported us again, we don't rewrite
|
||||
state._indecode = True
|
||||
@@ -267,7 +290,7 @@ def _make_rewritten_pyc(state, fn, pyc, co):
|
||||
os.rename(proc_pyc, pyc)
|
||||
|
||||
def _read_pyc(source, pyc):
|
||||
"""Possibly read a py.test pyc containing rewritten code.
|
||||
"""Possibly read a pytest pyc containing rewritten code.
|
||||
|
||||
Return rewritten code if successful or None if not.
|
||||
"""
|
||||
@@ -300,7 +323,7 @@ def rewrite_asserts(mod):
|
||||
|
||||
|
||||
_saferepr = py.io.saferepr
|
||||
from _pytest.assertion.util import format_explanation as _format_explanation
|
||||
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
|
||||
|
||||
def _should_repr_global_name(obj):
|
||||
return not hasattr(obj, "__name__") and not py.builtin.callable(obj)
|
||||
@@ -538,7 +561,8 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||
for i, v in enumerate(boolop.values):
|
||||
if i:
|
||||
fail_inner = []
|
||||
self.on_failure.append(ast.If(cond, fail_inner, []))
|
||||
# cond is set in a prior loop iteration below
|
||||
self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa
|
||||
self.on_failure = fail_inner
|
||||
self.push_format_context()
|
||||
res, expl = self.visit(v)
|
||||
@@ -631,7 +655,7 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||
res_expr = ast.Compare(left_res, [op], [next_res])
|
||||
self.statements.append(ast.Assign([store_names[i]], res_expr))
|
||||
left_res, left_expl = next_res, next_expl
|
||||
# Use py.code._reprcompare if that's available.
|
||||
# Use pytest.assertion.util._reprcompare if that's available.
|
||||
expl_call = self.helper("call_reprcompare",
|
||||
ast.Tuple(syms, ast.Load()),
|
||||
ast.Tuple(load_names, ast.Load()),
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
import py
|
||||
try:
|
||||
from collections.abc import Sequence
|
||||
from collections import Sequence
|
||||
except ImportError:
|
||||
try:
|
||||
from collections import Sequence
|
||||
except ImportError:
|
||||
Sequence = list
|
||||
|
||||
Sequence = list
|
||||
|
||||
BuiltinAssertionError = py.builtin.builtins.AssertionError
|
||||
u = py.builtin._totext
|
||||
|
||||
# The _reprcompare attribute on the util module is used by the new assertion
|
||||
# interpretation code and assertion rewriter to detect this plugin was
|
||||
@@ -29,7 +26,18 @@ def format_explanation(explanation):
|
||||
for when one explanation needs to span multiple lines, e.g. when
|
||||
displaying diffs.
|
||||
"""
|
||||
# simplify 'assert False where False = ...'
|
||||
explanation = _collapse_false(explanation)
|
||||
lines = _split_explanation(explanation)
|
||||
result = _format_lines(lines)
|
||||
return u('\n').join(result)
|
||||
|
||||
|
||||
def _collapse_false(explanation):
|
||||
"""Collapse expansions of False
|
||||
|
||||
So this strips out any "assert False\n{where False = ...\n}"
|
||||
blocks.
|
||||
"""
|
||||
where = 0
|
||||
while True:
|
||||
start = where = explanation.find("False\n{False = ", where)
|
||||
@@ -51,28 +59,48 @@ def format_explanation(explanation):
|
||||
explanation = (explanation[:start] + explanation[start+15:end-1] +
|
||||
explanation[end+1:])
|
||||
where -= 17
|
||||
raw_lines = (explanation or '').split('\n')
|
||||
# escape newlines not followed by {, } and ~
|
||||
return explanation
|
||||
|
||||
|
||||
def _split_explanation(explanation):
|
||||
"""Return a list of individual lines in the explanation
|
||||
|
||||
This will return a list of lines split on '\n{', '\n}' and '\n~'.
|
||||
Any other newlines will be escaped and appear in the line as the
|
||||
literal '\n' characters.
|
||||
"""
|
||||
raw_lines = (explanation or u('')).split('\n')
|
||||
lines = [raw_lines[0]]
|
||||
for l in raw_lines[1:]:
|
||||
if l.startswith('{') or l.startswith('}') or l.startswith('~'):
|
||||
lines.append(l)
|
||||
else:
|
||||
lines[-1] += '\\n' + l
|
||||
return lines
|
||||
|
||||
|
||||
def _format_lines(lines):
|
||||
"""Format the individual lines
|
||||
|
||||
This will replace the '{', '}' and '~' characters of our mini
|
||||
formatting language with the proper 'where ...', 'and ...' and ' +
|
||||
...' text, taking care of indentation along the way.
|
||||
|
||||
Return a list of formatted lines.
|
||||
"""
|
||||
result = lines[:1]
|
||||
stack = [0]
|
||||
stackcnt = [0]
|
||||
for line in lines[1:]:
|
||||
if line.startswith('{'):
|
||||
if stackcnt[-1]:
|
||||
s = 'and '
|
||||
s = u('and ')
|
||||
else:
|
||||
s = 'where '
|
||||
s = u('where ')
|
||||
stack.append(len(result))
|
||||
stackcnt[-1] += 1
|
||||
stackcnt.append(0)
|
||||
result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
|
||||
result.append(u(' +') + u(' ')*(len(stack)-1) + s + line[1:])
|
||||
elif line.startswith('}'):
|
||||
assert line.startswith('}')
|
||||
stack.pop()
|
||||
@@ -80,9 +108,9 @@ def format_explanation(explanation):
|
||||
result[stack[-1]] += line[1:]
|
||||
else:
|
||||
assert line.startswith('~')
|
||||
result.append(' '*len(stack) + line[1:])
|
||||
result.append(u(' ')*len(stack) + line[1:])
|
||||
assert len(stack) == 1
|
||||
return '\n'.join(result)
|
||||
return result
|
||||
|
||||
|
||||
# Provide basestring in python3
|
||||
@@ -97,7 +125,7 @@ def assertrepr_compare(config, op, left, right):
|
||||
width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
|
||||
left_repr = py.io.saferepr(left, maxsize=int(width/2))
|
||||
right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
|
||||
summary = '%s %s %s' % (left_repr, op, right_repr)
|
||||
summary = u('%s %s %s') % (left_repr, op, right_repr)
|
||||
|
||||
issequence = lambda x: (isinstance(x, (list, tuple, Sequence))
|
||||
and not isinstance(x, basestring))
|
||||
@@ -120,13 +148,11 @@ def assertrepr_compare(config, op, left, right):
|
||||
elif op == 'not in':
|
||||
if istext(left) and istext(right):
|
||||
explanation = _notin_text(left, right, verbose)
|
||||
except py.builtin._sysex:
|
||||
raise
|
||||
except:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
except Exception:
|
||||
explanation = [
|
||||
'(pytest_assertion plugin: representation of details failed. '
|
||||
'Probably an object has a faulty __repr__.)', str(excinfo)]
|
||||
u('(pytest_assertion plugin: representation of details failed. '
|
||||
'Probably an object has a faulty __repr__.)'),
|
||||
u(py.code.ExceptionInfo())]
|
||||
|
||||
if not explanation:
|
||||
return None
|
||||
@@ -135,12 +161,18 @@ def assertrepr_compare(config, op, left, right):
|
||||
|
||||
|
||||
def _diff_text(left, right, verbose=False):
|
||||
"""Return the explanation for the diff between text
|
||||
"""Return the explanation for the diff between text or bytes
|
||||
|
||||
Unless --verbose is used this will skip leading and trailing
|
||||
characters which are identical to keep the diff minimal.
|
||||
|
||||
If the input are bytes they will be safely converted to text.
|
||||
"""
|
||||
explanation = []
|
||||
if isinstance(left, py.builtin.bytes):
|
||||
left = u(repr(left)[1:-1]).replace(r'\n', '\n')
|
||||
if isinstance(right, py.builtin.bytes):
|
||||
right = u(repr(right)[1:-1]).replace(r'\n', '\n')
|
||||
if not verbose:
|
||||
i = 0 # just in case left or right has zero length
|
||||
for i in range(min(len(left), len(right))):
|
||||
@@ -148,8 +180,8 @@ def _diff_text(left, right, verbose=False):
|
||||
break
|
||||
if i > 42:
|
||||
i -= 10 # Provide some context
|
||||
explanation = ['Skipping %s identical leading '
|
||||
'characters in diff, use -v to show' % i]
|
||||
explanation = [u('Skipping %s identical leading '
|
||||
'characters in diff, use -v to show') % i]
|
||||
left = left[i:]
|
||||
right = right[i:]
|
||||
if len(left) == len(right):
|
||||
@@ -158,8 +190,8 @@ def _diff_text(left, right, verbose=False):
|
||||
break
|
||||
if i > 42:
|
||||
i -= 10 # Provide some context
|
||||
explanation += ['Skipping %s identical trailing '
|
||||
'characters in diff, use -v to show' % i]
|
||||
explanation += [u('Skipping %s identical trailing '
|
||||
'characters in diff, use -v to show') % i]
|
||||
left = left[:-i]
|
||||
right = right[:-i]
|
||||
explanation += [line.strip('\n')
|
||||
@@ -172,16 +204,15 @@ def _compare_eq_sequence(left, right, verbose=False):
|
||||
explanation = []
|
||||
for i in range(min(len(left), len(right))):
|
||||
if left[i] != right[i]:
|
||||
explanation += ['At index %s diff: %r != %r' %
|
||||
(i, left[i], right[i])]
|
||||
explanation += [u('At index %s diff: %r != %r')
|
||||
% (i, left[i], right[i])]
|
||||
break
|
||||
if len(left) > len(right):
|
||||
explanation += [
|
||||
'Left contains more items, first extra item: %s' %
|
||||
py.io.saferepr(left[len(right)],)]
|
||||
explanation += [u('Left contains more items, first extra item: %s')
|
||||
% py.io.saferepr(left[len(right)],)]
|
||||
elif len(left) < len(right):
|
||||
explanation += [
|
||||
'Right contains more items, first extra item: %s' %
|
||||
u('Right contains more items, first extra item: %s') %
|
||||
py.io.saferepr(right[len(left)],)]
|
||||
return explanation # + _diff_text(py.std.pprint.pformat(left),
|
||||
# py.std.pprint.pformat(right))
|
||||
@@ -192,11 +223,11 @@ def _compare_eq_set(left, right, verbose=False):
|
||||
diff_left = left - right
|
||||
diff_right = right - left
|
||||
if diff_left:
|
||||
explanation.append('Extra items in the left set:')
|
||||
explanation.append(u('Extra items in the left set:'))
|
||||
for item in diff_left:
|
||||
explanation.append(py.io.saferepr(item))
|
||||
if diff_right:
|
||||
explanation.append('Extra items in the right set:')
|
||||
explanation.append(u('Extra items in the right set:'))
|
||||
for item in diff_right:
|
||||
explanation.append(py.io.saferepr(item))
|
||||
return explanation
|
||||
@@ -207,25 +238,25 @@ def _compare_eq_dict(left, right, verbose=False):
|
||||
common = set(left).intersection(set(right))
|
||||
same = dict((k, left[k]) for k in common if left[k] == right[k])
|
||||
if same and not verbose:
|
||||
explanation += ['Omitting %s identical items, use -v to show' %
|
||||
explanation += [u('Omitting %s identical items, use -v to show') %
|
||||
len(same)]
|
||||
elif same:
|
||||
explanation += ['Common items:']
|
||||
explanation += [u('Common items:')]
|
||||
explanation += py.std.pprint.pformat(same).splitlines()
|
||||
diff = set(k for k in common if left[k] != right[k])
|
||||
if diff:
|
||||
explanation += ['Differing items:']
|
||||
explanation += [u('Differing items:')]
|
||||
for k in diff:
|
||||
explanation += [py.io.saferepr({k: left[k]}) + ' != ' +
|
||||
py.io.saferepr({k: right[k]})]
|
||||
extra_left = set(left) - set(right)
|
||||
if extra_left:
|
||||
explanation.append('Left contains more items:')
|
||||
explanation.append(u('Left contains more items:'))
|
||||
explanation.extend(py.std.pprint.pformat(
|
||||
dict((k, left[k]) for k in extra_left)).splitlines())
|
||||
extra_right = set(right) - set(left)
|
||||
if extra_right:
|
||||
explanation.append('Right contains more items:')
|
||||
explanation.append(u('Right contains more items:'))
|
||||
explanation.extend(py.std.pprint.pformat(
|
||||
dict((k, right[k]) for k in extra_right)).splitlines())
|
||||
return explanation
|
||||
@@ -237,14 +268,14 @@ def _notin_text(term, text, verbose=False):
|
||||
tail = text[index+len(term):]
|
||||
correct_text = head + tail
|
||||
diff = _diff_text(correct_text, text, verbose)
|
||||
newdiff = ['%s is contained here:' % py.io.saferepr(term, maxsize=42)]
|
||||
newdiff = [u('%s is contained here:') % py.io.saferepr(term, maxsize=42)]
|
||||
for line in diff:
|
||||
if line.startswith('Skipping'):
|
||||
if line.startswith(u('Skipping')):
|
||||
continue
|
||||
if line.startswith('- '):
|
||||
if line.startswith(u('- ')):
|
||||
continue
|
||||
if line.startswith('+ '):
|
||||
newdiff.append(' ' + line[2:])
|
||||
if line.startswith(u('+ ')):
|
||||
newdiff.append(u(' ') + line[2:])
|
||||
else:
|
||||
newdiff.append(line)
|
||||
return newdiff
|
||||
|
||||
@@ -1,42 +1,54 @@
|
||||
""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
|
||||
"""
|
||||
per-test stdout/stderr capturing mechanism.
|
||||
|
||||
"""
|
||||
from __future__ import with_statement
|
||||
|
||||
import pytest, py
|
||||
import sys
|
||||
import os
|
||||
from tempfile import TemporaryFile
|
||||
|
||||
import py
|
||||
import pytest
|
||||
|
||||
from py.io import TextIO
|
||||
unicode = py.builtin.text
|
||||
|
||||
patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("general")
|
||||
group._addoption('--capture', action="store", default=None,
|
||||
group._addoption(
|
||||
'--capture', action="store",
|
||||
default="fd" if hasattr(os, "dup") else "sys",
|
||||
metavar="method", choices=['fd', 'sys', 'no'],
|
||||
help="per-test capturing method: one of fd (default)|sys|no.")
|
||||
group._addoption('-s', action="store_const", const="no", dest="capture",
|
||||
help="per-test capturing method: one of fd|sys|no.")
|
||||
group._addoption(
|
||||
'-s', action="store_const", const="no", dest="capture",
|
||||
help="shortcut for --capture=no.")
|
||||
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
|
||||
ns = parser.parse_known_args(args)
|
||||
method = ns.capture
|
||||
if not method:
|
||||
method = "fd"
|
||||
if method == "fd" and not hasattr(os, "dup"):
|
||||
method = "sys"
|
||||
capman = CaptureManager(method)
|
||||
early_config.pluginmanager.register(capman, "capturemanager")
|
||||
ns = early_config.known_args_namespace
|
||||
pluginmanager = early_config.pluginmanager
|
||||
if ns.capture == "no":
|
||||
return
|
||||
capman = CaptureManager(ns.capture)
|
||||
pluginmanager.register(capman, "capturemanager")
|
||||
|
||||
# make sure that capturemanager is properly reset at final shutdown
|
||||
def teardown():
|
||||
try:
|
||||
capman.reset_capturings()
|
||||
except ValueError:
|
||||
pass
|
||||
early_config.pluginmanager.add_shutdown(teardown)
|
||||
pluginmanager.add_shutdown(capman.reset_capturings)
|
||||
|
||||
# make sure logging does not raise exceptions at the end
|
||||
def silence_logging_at_shutdown():
|
||||
if "logging" in sys.modules:
|
||||
sys.modules["logging"].raiseExceptions = False
|
||||
early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown)
|
||||
pluginmanager.add_shutdown(silence_logging_at_shutdown)
|
||||
|
||||
# finally trigger conftest loading but while capturing (issue93)
|
||||
capman.resumecapture()
|
||||
capman.init_capturings()
|
||||
try:
|
||||
try:
|
||||
return __multicall__.execute()
|
||||
@@ -47,202 +59,387 @@ def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
|
||||
sys.stderr.write(err)
|
||||
raise
|
||||
|
||||
def addouterr(rep, outerr):
|
||||
for secname, content in zip(["out", "err"], outerr):
|
||||
if content:
|
||||
rep.sections.append(("Captured std%s" % secname, content))
|
||||
|
||||
class NoCapture:
|
||||
def startall(self):
|
||||
pass
|
||||
def resume(self):
|
||||
pass
|
||||
def reset(self):
|
||||
pass
|
||||
def suspend(self):
|
||||
return "", ""
|
||||
|
||||
class CaptureManager:
|
||||
def __init__(self, defaultmethod=None):
|
||||
self._method2capture = {}
|
||||
self._defaultmethod = defaultmethod
|
||||
|
||||
def _maketempfile(self):
|
||||
f = py.std.tempfile.TemporaryFile()
|
||||
newf = py.io.dupfile(f, encoding="UTF-8")
|
||||
f.close()
|
||||
return newf
|
||||
|
||||
def _makestringio(self):
|
||||
return py.io.TextIO()
|
||||
def __init__(self, method):
|
||||
self._method = method
|
||||
|
||||
def _getcapture(self, method):
|
||||
if method == "fd":
|
||||
return py.io.StdCaptureFD(now=False,
|
||||
out=self._maketempfile(), err=self._maketempfile()
|
||||
)
|
||||
return MultiCapture(out=True, err=True, Capture=FDCapture)
|
||||
elif method == "sys":
|
||||
return py.io.StdCapture(now=False,
|
||||
out=self._makestringio(), err=self._makestringio()
|
||||
)
|
||||
return MultiCapture(out=True, err=True, Capture=SysCapture)
|
||||
elif method == "no":
|
||||
return NoCapture()
|
||||
return MultiCapture(out=False, err=False, in_=False)
|
||||
else:
|
||||
raise ValueError("unknown capturing method: %r" % method)
|
||||
|
||||
def _getmethod(self, config, fspath):
|
||||
if config.option.capture:
|
||||
method = config.option.capture
|
||||
else:
|
||||
try:
|
||||
method = config._conftest.rget("option_capture", path=fspath)
|
||||
except KeyError:
|
||||
method = "fd"
|
||||
if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
|
||||
method = "sys"
|
||||
return method
|
||||
def init_capturings(self):
|
||||
assert not hasattr(self, "_capturing")
|
||||
self._capturing = self._getcapture(self._method)
|
||||
self._capturing.start_capturing()
|
||||
|
||||
def reset_capturings(self):
|
||||
for name, cap in self._method2capture.items():
|
||||
cap.reset()
|
||||
cap = self.__dict__.pop("_capturing", None)
|
||||
if cap is not None:
|
||||
cap.pop_outerr_to_orig()
|
||||
cap.stop_capturing()
|
||||
|
||||
def resumecapture_item(self, item):
|
||||
method = self._getmethod(item.config, item.fspath)
|
||||
if not hasattr(item, 'outerr'):
|
||||
item.outerr = ('', '') # we accumulate outerr on the item
|
||||
return self.resumecapture(method)
|
||||
def resumecapture(self):
|
||||
self._capturing.resume_capturing()
|
||||
|
||||
def resumecapture(self, method=None):
|
||||
if hasattr(self, '_capturing'):
|
||||
raise ValueError("cannot resume, already capturing with %r" %
|
||||
(self._capturing,))
|
||||
if method is None:
|
||||
method = self._defaultmethod
|
||||
cap = self._method2capture.get(method)
|
||||
self._capturing = method
|
||||
if cap is None:
|
||||
self._method2capture[method] = cap = self._getcapture(method)
|
||||
cap.startall()
|
||||
else:
|
||||
cap.resume()
|
||||
|
||||
def suspendcapture(self, item=None):
|
||||
def suspendcapture(self, in_=False):
|
||||
self.deactivate_funcargs()
|
||||
if hasattr(self, '_capturing'):
|
||||
method = self._capturing
|
||||
cap = self._method2capture.get(method)
|
||||
if cap is not None:
|
||||
outerr = cap.suspend()
|
||||
del self._capturing
|
||||
if item:
|
||||
outerr = (item.outerr[0] + outerr[0],
|
||||
item.outerr[1] + outerr[1])
|
||||
cap = getattr(self, "_capturing", None)
|
||||
if cap is not None:
|
||||
outerr = cap.readouterr()
|
||||
cap.suspend_capturing(in_=in_)
|
||||
return outerr
|
||||
if hasattr(item, 'outerr'):
|
||||
return item.outerr
|
||||
return "", ""
|
||||
|
||||
def activate_funcargs(self, pyfuncitem):
|
||||
funcargs = getattr(pyfuncitem, "funcargs", None)
|
||||
if funcargs is not None:
|
||||
for name, capfuncarg in funcargs.items():
|
||||
if name in ('capsys', 'capfd'):
|
||||
assert not hasattr(self, '_capturing_funcarg')
|
||||
self._capturing_funcarg = capfuncarg
|
||||
capfuncarg._start()
|
||||
capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
|
||||
if capfuncarg is not None:
|
||||
capfuncarg._start()
|
||||
self._capfuncarg = capfuncarg
|
||||
|
||||
def deactivate_funcargs(self):
|
||||
capturing_funcarg = getattr(self, '_capturing_funcarg', None)
|
||||
if capturing_funcarg:
|
||||
outerr = capturing_funcarg._finalize()
|
||||
del self._capturing_funcarg
|
||||
return outerr
|
||||
capfuncarg = self.__dict__.pop("_capfuncarg", None)
|
||||
if capfuncarg is not None:
|
||||
capfuncarg.close()
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_make_collect_report(self, __multicall__, collector):
|
||||
method = self._getmethod(collector.config, collector.fspath)
|
||||
try:
|
||||
self.resumecapture(method)
|
||||
except ValueError:
|
||||
return # recursive collect, XXX refactor capturing
|
||||
# to allow for more lightweight recursive capturing
|
||||
if not isinstance(collector, pytest.File):
|
||||
return
|
||||
self.resumecapture()
|
||||
try:
|
||||
rep = __multicall__.execute()
|
||||
finally:
|
||||
outerr = self.suspendcapture()
|
||||
addouterr(rep, outerr)
|
||||
out, err = self.suspendcapture()
|
||||
if out:
|
||||
rep.sections.append(("Captured stdout", out))
|
||||
if err:
|
||||
rep.sections.append(("Captured stderr", err))
|
||||
return rep
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.mark.hookwrapper
|
||||
def pytest_runtest_setup(self, item):
|
||||
self.resumecapture_item(item)
|
||||
self.resumecapture()
|
||||
yield
|
||||
self.suspendcapture_item(item, "setup")
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.mark.hookwrapper
|
||||
def pytest_runtest_call(self, item):
|
||||
self.resumecapture_item(item)
|
||||
self.resumecapture()
|
||||
self.activate_funcargs(item)
|
||||
yield
|
||||
#self.deactivate_funcargs() called from suspendcapture()
|
||||
self.suspendcapture_item(item, "call")
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
@pytest.mark.hookwrapper
|
||||
def pytest_runtest_teardown(self, item):
|
||||
self.resumecapture_item(item)
|
||||
|
||||
def pytest_keyboard_interrupt(self, excinfo):
|
||||
if hasattr(self, '_capturing'):
|
||||
self.suspendcapture()
|
||||
self.resumecapture()
|
||||
yield
|
||||
self.suspendcapture_item(item, "teardown")
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_runtest_makereport(self, __multicall__, item, call):
|
||||
funcarg_outerr = self.deactivate_funcargs()
|
||||
rep = __multicall__.execute()
|
||||
outerr = self.suspendcapture(item)
|
||||
if funcarg_outerr is not None:
|
||||
outerr = (outerr[0] + funcarg_outerr[0],
|
||||
outerr[1] + funcarg_outerr[1])
|
||||
addouterr(rep, outerr)
|
||||
if not rep.passed or rep.when == "teardown":
|
||||
outerr = ('', '')
|
||||
item.outerr = outerr
|
||||
return rep
|
||||
def pytest_keyboard_interrupt(self, excinfo):
|
||||
self.reset_capturings()
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_internalerror(self, excinfo):
|
||||
self.reset_capturings()
|
||||
|
||||
def suspendcapture_item(self, item, when):
|
||||
out, err = self.suspendcapture()
|
||||
item.add_report_section(when, "out", out)
|
||||
item.add_report_section(when, "err", err)
|
||||
|
||||
error_capsysfderror = "cannot use capsys and capfd at the same time"
|
||||
|
||||
def pytest_funcarg__capsys(request):
|
||||
|
||||
@pytest.fixture
|
||||
def capsys(request):
|
||||
"""enables capturing of writes to sys.stdout/sys.stderr and makes
|
||||
captured output available via ``capsys.readouterr()`` method calls
|
||||
which return a ``(out, err)`` tuple.
|
||||
"""
|
||||
if "capfd" in request._funcargs:
|
||||
raise request.raiseerror(error_capsysfderror)
|
||||
return CaptureFixture(py.io.StdCapture)
|
||||
request.node._capfuncarg = c = CaptureFixture(SysCapture)
|
||||
return c
|
||||
|
||||
def pytest_funcarg__capfd(request):
|
||||
@pytest.fixture
|
||||
def capfd(request):
|
||||
"""enables capturing of writes to file descriptors 1 and 2 and makes
|
||||
captured output available via ``capsys.readouterr()`` method calls
|
||||
captured output available via ``capfd.readouterr()`` method calls
|
||||
which return a ``(out, err)`` tuple.
|
||||
"""
|
||||
if "capsys" in request._funcargs:
|
||||
request.raiseerror(error_capsysfderror)
|
||||
if not hasattr(os, 'dup'):
|
||||
pytest.skip("capfd funcarg needs os.dup")
|
||||
return CaptureFixture(py.io.StdCaptureFD)
|
||||
request.node._capfuncarg = c = CaptureFixture(FDCapture)
|
||||
return c
|
||||
|
||||
|
||||
class CaptureFixture:
|
||||
def __init__(self, captureclass):
|
||||
self.capture = captureclass(now=False)
|
||||
self.captureclass = captureclass
|
||||
|
||||
def _start(self):
|
||||
self.capture.startall()
|
||||
self._capture = MultiCapture(out=True, err=True, in_=False,
|
||||
Capture=self.captureclass)
|
||||
self._capture.start_capturing()
|
||||
|
||||
def _finalize(self):
|
||||
if hasattr(self, 'capture'):
|
||||
outerr = self._outerr = self.capture.reset()
|
||||
del self.capture
|
||||
return outerr
|
||||
def close(self):
|
||||
cap = self.__dict__.pop("_capture", None)
|
||||
if cap is not None:
|
||||
self._outerr = cap.pop_outerr_to_orig()
|
||||
cap.stop_capturing()
|
||||
|
||||
def readouterr(self):
|
||||
try:
|
||||
return self.capture.readouterr()
|
||||
return self._capture.readouterr()
|
||||
except AttributeError:
|
||||
return self._outerr
|
||||
|
||||
|
||||
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
|
||||
""" return a open text file object that's a duplicate of f on the
|
||||
FD-level if possible.
|
||||
"""
|
||||
encoding = getattr(f, "encoding", None)
|
||||
try:
|
||||
fd = f.fileno()
|
||||
except Exception:
|
||||
if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"):
|
||||
# we seem to have a text stream, let's just use it
|
||||
return f
|
||||
else:
|
||||
newfd = os.dup(fd)
|
||||
if "b" not in mode:
|
||||
mode += "b"
|
||||
f = os.fdopen(newfd, mode, 0) # no buffering
|
||||
return EncodedFile(f, encoding or default_encoding)
|
||||
|
||||
|
||||
class EncodedFile(object):
|
||||
def __init__(self, buffer, encoding):
|
||||
self.buffer = buffer
|
||||
self.encoding = encoding
|
||||
|
||||
def write(self, obj):
|
||||
if isinstance(obj, unicode):
|
||||
obj = obj.encode(self.encoding, "replace")
|
||||
self.buffer.write(obj)
|
||||
|
||||
def writelines(self, linelist):
|
||||
data = ''.join(linelist)
|
||||
self.write(data)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.buffer, name)
|
||||
|
||||
|
||||
class MultiCapture(object):
|
||||
out = err = in_ = None
|
||||
|
||||
def __init__(self, out=True, err=True, in_=True, Capture=None):
|
||||
if in_:
|
||||
self.in_ = Capture(0)
|
||||
if out:
|
||||
self.out = Capture(1)
|
||||
if err:
|
||||
self.err = Capture(2)
|
||||
|
||||
def start_capturing(self):
|
||||
if self.in_:
|
||||
self.in_.start()
|
||||
if self.out:
|
||||
self.out.start()
|
||||
if self.err:
|
||||
self.err.start()
|
||||
|
||||
def pop_outerr_to_orig(self):
|
||||
""" pop current snapshot out/err capture and flush to orig streams. """
|
||||
out, err = self.readouterr()
|
||||
if out:
|
||||
self.out.writeorg(out)
|
||||
if err:
|
||||
self.err.writeorg(err)
|
||||
return out, err
|
||||
|
||||
def suspend_capturing(self, in_=False):
|
||||
if self.out:
|
||||
self.out.suspend()
|
||||
if self.err:
|
||||
self.err.suspend()
|
||||
if in_ and self.in_:
|
||||
self.in_.suspend()
|
||||
self._in_suspended = True
|
||||
|
||||
def resume_capturing(self):
|
||||
if self.out:
|
||||
self.out.resume()
|
||||
if self.err:
|
||||
self.err.resume()
|
||||
if hasattr(self, "_in_suspended"):
|
||||
self.in_.resume()
|
||||
del self._in_suspended
|
||||
|
||||
def stop_capturing(self):
|
||||
""" stop capturing and reset capturing streams """
|
||||
if hasattr(self, '_reset'):
|
||||
raise ValueError("was already stopped")
|
||||
self._reset = True
|
||||
if self.out:
|
||||
self.out.done()
|
||||
if self.err:
|
||||
self.err.done()
|
||||
if self.in_:
|
||||
self.in_.done()
|
||||
|
||||
def readouterr(self):
|
||||
""" return snapshot unicode value of stdout/stderr capturings. """
|
||||
return (self.out.snap() if self.out is not None else "",
|
||||
self.err.snap() if self.err is not None else "")
|
||||
|
||||
class NoCapture:
|
||||
__init__ = start = done = suspend = resume = lambda *args: None
|
||||
|
||||
class FDCapture:
|
||||
""" Capture IO to/from a given os-level filedescriptor. """
|
||||
|
||||
def __init__(self, targetfd, tmpfile=None):
|
||||
self.targetfd = targetfd
|
||||
try:
|
||||
self.targetfd_save = os.dup(self.targetfd)
|
||||
except OSError:
|
||||
self.start = lambda: None
|
||||
self.done = lambda: None
|
||||
else:
|
||||
if targetfd == 0:
|
||||
assert not tmpfile, "cannot set tmpfile with stdin"
|
||||
tmpfile = open(os.devnull, "r")
|
||||
self.syscapture = SysCapture(targetfd)
|
||||
else:
|
||||
if tmpfile is None:
|
||||
f = TemporaryFile()
|
||||
with f:
|
||||
tmpfile = safe_text_dupfile(f, mode="wb+")
|
||||
if targetfd in patchsysdict:
|
||||
self.syscapture = SysCapture(targetfd, tmpfile)
|
||||
else:
|
||||
self.syscapture = NoCapture()
|
||||
self.tmpfile = tmpfile
|
||||
self.tmpfile_fd = tmpfile.fileno()
|
||||
|
||||
def __repr__(self):
|
||||
return "<FDCapture %s oldfd=%s>" % (self.targetfd, self.targetfd_save)
|
||||
|
||||
def start(self):
|
||||
""" Start capturing on targetfd using memorized tmpfile. """
|
||||
try:
|
||||
os.fstat(self.targetfd_save)
|
||||
except (AttributeError, OSError):
|
||||
raise ValueError("saved filedescriptor not valid anymore")
|
||||
os.dup2(self.tmpfile_fd, self.targetfd)
|
||||
self.syscapture.start()
|
||||
|
||||
def snap(self):
|
||||
f = self.tmpfile
|
||||
f.seek(0)
|
||||
res = f.read()
|
||||
if res:
|
||||
enc = getattr(f, "encoding", None)
|
||||
if enc and isinstance(res, bytes):
|
||||
res = py.builtin._totext(res, enc, "replace")
|
||||
f.truncate(0)
|
||||
f.seek(0)
|
||||
return res
|
||||
return ''
|
||||
|
||||
def done(self):
|
||||
""" stop capturing, restore streams, return original capture file,
|
||||
seeked to position zero. """
|
||||
targetfd_save = self.__dict__.pop("targetfd_save")
|
||||
os.dup2(targetfd_save, self.targetfd)
|
||||
os.close(targetfd_save)
|
||||
self.syscapture.done()
|
||||
self.tmpfile.close()
|
||||
|
||||
def suspend(self):
|
||||
self.syscapture.suspend()
|
||||
os.dup2(self.targetfd_save, self.targetfd)
|
||||
|
||||
def resume(self):
|
||||
self.syscapture.resume()
|
||||
os.dup2(self.tmpfile_fd, self.targetfd)
|
||||
|
||||
def writeorg(self, data):
|
||||
""" write to original file descriptor. """
|
||||
if py.builtin._istext(data):
|
||||
data = data.encode("utf8") # XXX use encoding of original stream
|
||||
os.write(self.targetfd_save, data)
|
||||
|
||||
|
||||
class SysCapture:
|
||||
def __init__(self, fd, tmpfile=None):
|
||||
name = patchsysdict[fd]
|
||||
self._old = getattr(sys, name)
|
||||
self.name = name
|
||||
if tmpfile is None:
|
||||
if name == "stdin":
|
||||
tmpfile = DontReadFromInput()
|
||||
else:
|
||||
tmpfile = TextIO()
|
||||
self.tmpfile = tmpfile
|
||||
|
||||
def start(self):
|
||||
setattr(sys, self.name, self.tmpfile)
|
||||
|
||||
def snap(self):
|
||||
f = self.tmpfile
|
||||
res = f.getvalue()
|
||||
f.truncate(0)
|
||||
f.seek(0)
|
||||
return res
|
||||
|
||||
def done(self):
|
||||
setattr(sys, self.name, self._old)
|
||||
del self._old
|
||||
self.tmpfile.close()
|
||||
|
||||
def suspend(self):
|
||||
setattr(sys, self.name, self._old)
|
||||
|
||||
def resume(self):
|
||||
setattr(sys, self.name, self.tmpfile)
|
||||
|
||||
def writeorg(self, data):
|
||||
self._old.write(data)
|
||||
self._old.flush()
|
||||
|
||||
|
||||
class DontReadFromInput:
|
||||
"""Temporary stub class. Ideally when stdin is accessed, the
|
||||
capturing should be turned off, with possibly all data captured
|
||||
so far sent to the screen. This should be configurable, though,
|
||||
because in automated test runs it is better to crash than
|
||||
hang indefinitely.
|
||||
"""
|
||||
def read(self, *args):
|
||||
raise IOError("reading from stdin while output is captured")
|
||||
readline = read
|
||||
readlines = read
|
||||
__iter__ = read
|
||||
|
||||
def fileno(self):
|
||||
raise ValueError("redirected Stdin is pseudofile, has no fileno()")
|
||||
|
||||
def isatty(self):
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
self._finalize()
|
||||
pass
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
""" command line options, ini-file and conftest.py processing. """
|
||||
|
||||
import py
|
||||
# DON't import pytest here because it causes import cycle troubles
|
||||
import re
|
||||
import sys, os
|
||||
from _pytest import hookspec # the extension point definitions
|
||||
from _pytest.core import PluginManager
|
||||
|
||||
# pytest startup
|
||||
#
|
||||
class ConftestImportFailure(Exception):
|
||||
def __init__(self, path, excinfo):
|
||||
Exception.__init__(self, path, excinfo)
|
||||
self.path = path
|
||||
self.excinfo = excinfo
|
||||
|
||||
|
||||
def main(args=None, plugins=None):
|
||||
""" return exit code, after performing an in-process test run.
|
||||
@@ -15,15 +24,23 @@ def main(args=None, plugins=None):
|
||||
:arg plugins: list of plugin objects to be auto-registered during
|
||||
initialization.
|
||||
"""
|
||||
config = _prepareconfig(args, plugins)
|
||||
exitstatus = config.hook.pytest_cmdline_main(config=config)
|
||||
return exitstatus
|
||||
try:
|
||||
config = _prepareconfig(args, plugins)
|
||||
except ConftestImportFailure:
|
||||
e = sys.exc_info()[1]
|
||||
tw = py.io.TerminalWriter(sys.stderr)
|
||||
for line in py.std.traceback.format_exception(*e.excinfo):
|
||||
tw.line(line.rstrip(), red=True)
|
||||
tw.line("ERROR: could not load %s\n" % (e.path), red=True)
|
||||
return 4
|
||||
else:
|
||||
return config.hook.pytest_cmdline_main(config=config)
|
||||
|
||||
class cmdline: # compatibility namespace
|
||||
main = staticmethod(main)
|
||||
|
||||
class UsageError(Exception):
|
||||
""" error in py.test usage or invocation"""
|
||||
""" error in pytest usage or invocation"""
|
||||
|
||||
_preinit = []
|
||||
|
||||
@@ -41,7 +58,7 @@ def get_plugin_manager():
|
||||
return _preinit.pop(0)
|
||||
# subsequent calls to main will create a fresh instance
|
||||
pluginmanager = PytestPluginManager()
|
||||
pluginmanager.config = config = Config(pluginmanager) # XXX attr needed?
|
||||
pluginmanager.config = Config(pluginmanager) # XXX attr needed?
|
||||
for spec in default_plugins:
|
||||
pluginmanager.import_plugin(spec)
|
||||
return pluginmanager
|
||||
@@ -56,11 +73,15 @@ def _prepareconfig(args=None, plugins=None):
|
||||
raise ValueError("not a string or argument list: %r" % (args,))
|
||||
args = py.std.shlex.split(args)
|
||||
pluginmanager = get_plugin_manager()
|
||||
if plugins:
|
||||
for plugin in plugins:
|
||||
pluginmanager.register(plugin)
|
||||
return pluginmanager.hook.pytest_cmdline_parse(
|
||||
pluginmanager=pluginmanager, args=args)
|
||||
try:
|
||||
if plugins:
|
||||
for plugin in plugins:
|
||||
pluginmanager.register(plugin)
|
||||
return pluginmanager.hook.pytest_cmdline_parse(
|
||||
pluginmanager=pluginmanager, args=args)
|
||||
except Exception:
|
||||
pluginmanager.ensure_shutdown()
|
||||
raise
|
||||
|
||||
class PytestPluginManager(PluginManager):
|
||||
def __init__(self, hookspecs=[hookspec]):
|
||||
@@ -82,6 +103,8 @@ class PytestPluginManager(PluginManager):
|
||||
config.addinivalue_line("markers",
|
||||
"trylast: mark a hook implementation function such that the "
|
||||
"plugin machinery will try to call it last/as late as possible.")
|
||||
for warning in self._warnings:
|
||||
config.warn(code="I1", message=warning)
|
||||
|
||||
|
||||
class Parser:
|
||||
@@ -94,7 +117,6 @@ class Parser:
|
||||
self._usage = usage
|
||||
self._inidict = {}
|
||||
self._ininames = []
|
||||
self.hints = []
|
||||
|
||||
def processoption(self, option):
|
||||
if self._processopt:
|
||||
@@ -129,8 +151,8 @@ class Parser:
|
||||
|
||||
:opts: option names, can be short or long options.
|
||||
:attrs: same attributes which the ``add_option()`` function of the
|
||||
`optparse library
|
||||
<http://docs.python.org/library/optparse.html#module-optparse>`_
|
||||
`argparse library
|
||||
<http://docs.python.org/2/library/argparse.html>`_
|
||||
accepts.
|
||||
|
||||
After command line parsing options are available on the pytest config
|
||||
@@ -159,7 +181,7 @@ class Parser:
|
||||
a = option.attrs()
|
||||
arggroup.add_argument(*n, **a)
|
||||
# bash like autocompletion for dirs (appending '/')
|
||||
optparser.add_argument(FILE_OR_DIR, nargs='*'
|
||||
optparser.add_argument(FILE_OR_DIR, nargs='*', type=node_with_line_number,
|
||||
).completer=filescompleter
|
||||
return optparser
|
||||
|
||||
@@ -226,7 +248,7 @@ class Argument:
|
||||
help = attrs['help']
|
||||
if '%default' in help:
|
||||
py.std.warnings.warn(
|
||||
'py.test now uses argparse. "%default" should be'
|
||||
'pytest now uses argparse. "%default" should be'
|
||||
' changed to "%(default)s" ',
|
||||
FutureWarning,
|
||||
stacklevel=3)
|
||||
@@ -379,14 +401,6 @@ class MyOptionParser(py.std.argparse.ArgumentParser):
|
||||
py.std.argparse.ArgumentParser.__init__(self, usage=parser._usage,
|
||||
add_help=False, formatter_class=DropShorterLongHelpFormatter)
|
||||
|
||||
def format_epilog(self, formatter):
|
||||
hints = self._parser.hints
|
||||
if hints:
|
||||
s = "\n".join(["hint: " + x for x in hints]) + "\n"
|
||||
s = "\n" + s + "\n"
|
||||
return s
|
||||
return ""
|
||||
|
||||
def parse_args(self, args=None, namespace=None):
|
||||
"""allow splitting of positional arguments"""
|
||||
args, argv = self.parse_known_args(args, namespace)
|
||||
@@ -442,47 +456,36 @@ class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
|
||||
if len(option) == 2 or option[2] == ' ':
|
||||
return_list.append(option)
|
||||
if option[2:] == short_long.get(option.replace('-', '')):
|
||||
return_list.append(option)
|
||||
return_list.append(option.replace(' ', '='))
|
||||
action._formatted_action_invocation = ', '.join(return_list)
|
||||
return action._formatted_action_invocation
|
||||
|
||||
|
||||
class Conftest(object):
|
||||
""" the single place for accessing values and interacting
|
||||
towards conftest modules from py.test objects.
|
||||
towards conftest modules from pytest objects.
|
||||
"""
|
||||
def __init__(self, onimport=None, confcutdir=None):
|
||||
def __init__(self, onimport=None):
|
||||
self._path2confmods = {}
|
||||
self._onimport = onimport
|
||||
self._conftestpath2mod = {}
|
||||
self._confcutdir = confcutdir
|
||||
self._confcutdir = None
|
||||
|
||||
def setinitial(self, args):
|
||||
""" try to find a first anchor path for looking up global values
|
||||
from conftests. This function is usually called _before_
|
||||
argument parsing. conftest files may add command line options
|
||||
and we thus have no completely safe way of determining
|
||||
which parts of the arguments are actually related to options
|
||||
and which are file system paths. We just try here to get
|
||||
bootstrapped ...
|
||||
def setinitial(self, namespace):
|
||||
""" load initial conftest files given a preparsed "namespace".
|
||||
As conftest files may add their own command line options
|
||||
which have arguments ('--my-opt somepath') we might get some
|
||||
false positives. All builtin and 3rd party plugins will have
|
||||
been loaded, however, so common options will not confuse our logic
|
||||
here.
|
||||
"""
|
||||
current = py.path.local()
|
||||
opt = '--confcutdir'
|
||||
for i in range(len(args)):
|
||||
opt1 = str(args[i])
|
||||
if opt1.startswith(opt):
|
||||
if opt1 == opt:
|
||||
if len(args) > i:
|
||||
p = current.join(args[i+1], abs=True)
|
||||
elif opt1.startswith(opt + "="):
|
||||
p = current.join(opt1[len(opt)+1:], abs=1)
|
||||
self._confcutdir = p
|
||||
break
|
||||
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
|
||||
if namespace.confcutdir else None
|
||||
testpaths = namespace.file_or_dir
|
||||
foundanchor = False
|
||||
for arg in args:
|
||||
if hasattr(arg, 'startswith') and arg.startswith("--"):
|
||||
continue
|
||||
anchor = current.join(arg, abs=1)
|
||||
for path in testpaths:
|
||||
anchor = current.join(path, abs=1)
|
||||
if exists(anchor): # we found some file object
|
||||
self._try_load_conftest(anchor)
|
||||
foundanchor = True
|
||||
@@ -490,7 +493,7 @@ class Conftest(object):
|
||||
self._try_load_conftest(current)
|
||||
|
||||
def _try_load_conftest(self, anchor):
|
||||
self._path2confmods[None] = self.getconftestmodules(anchor)
|
||||
self.getconftestmodules(anchor)
|
||||
# let's also consider test* subdirs
|
||||
if anchor.check(dir=1):
|
||||
for x in anchor.listdir("test*"):
|
||||
@@ -499,28 +502,22 @@ class Conftest(object):
|
||||
|
||||
def getconftestmodules(self, path):
|
||||
try:
|
||||
clist = self._path2confmods[path]
|
||||
return self._path2confmods[path]
|
||||
except KeyError:
|
||||
if path is None:
|
||||
raise ValueError("missing default conftest.")
|
||||
clist = []
|
||||
for parent in path.parts():
|
||||
if self._confcutdir and self._confcutdir.relto(parent):
|
||||
continue
|
||||
conftestpath = parent.join("conftest.py")
|
||||
if conftestpath.check(file=1):
|
||||
clist.append(self.importconftest(conftestpath))
|
||||
mod = self.importconftest(conftestpath)
|
||||
clist.append(mod)
|
||||
self._path2confmods[path] = clist
|
||||
return clist
|
||||
return clist
|
||||
|
||||
def rget(self, name, path=None):
|
||||
mod, value = self.rget_with_confmod(name, path)
|
||||
return value
|
||||
|
||||
def rget_with_confmod(self, name, path=None):
|
||||
def rget_with_confmod(self, name, path):
|
||||
modules = self.getconftestmodules(path)
|
||||
modules.reverse()
|
||||
for mod in modules:
|
||||
for mod in reversed(modules):
|
||||
try:
|
||||
return mod, getattr(mod, name)
|
||||
except AttributeError:
|
||||
@@ -528,27 +525,27 @@ class Conftest(object):
|
||||
raise KeyError(name)
|
||||
|
||||
def importconftest(self, conftestpath):
|
||||
assert conftestpath.check(), conftestpath
|
||||
try:
|
||||
return self._conftestpath2mod[conftestpath]
|
||||
except KeyError:
|
||||
pkgpath = conftestpath.pypkgpath()
|
||||
if pkgpath is None:
|
||||
_ensure_removed_sysmodule(conftestpath.purebasename)
|
||||
self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
|
||||
try:
|
||||
mod = conftestpath.pyimport()
|
||||
except Exception:
|
||||
raise ConftestImportFailure(conftestpath, sys.exc_info())
|
||||
self._conftestpath2mod[conftestpath] = mod
|
||||
dirpath = conftestpath.dirpath()
|
||||
if dirpath in self._path2confmods:
|
||||
for path, mods in self._path2confmods.items():
|
||||
if path and path.relto(dirpath) or path == dirpath:
|
||||
assert mod not in mods
|
||||
mods.append(mod)
|
||||
self._postimport(mod)
|
||||
if self._onimport:
|
||||
self._onimport(mod)
|
||||
return mod
|
||||
|
||||
def _postimport(self, mod):
|
||||
if self._onimport:
|
||||
self._onimport(mod)
|
||||
return mod
|
||||
|
||||
def _ensure_removed_sysmodule(modname):
|
||||
try:
|
||||
@@ -563,6 +560,11 @@ class CmdOptions(object):
|
||||
def __repr__(self):
|
||||
return "<CmdOptions %r>" %(self.__dict__,)
|
||||
|
||||
class Notset:
|
||||
def __repr__(self):
|
||||
return "<NOTSET>"
|
||||
|
||||
notset = Notset()
|
||||
FILE_OR_DIR = 'file_or_dir'
|
||||
class Config(object):
|
||||
""" access to configuration values, pluginmanager and plugin hooks. """
|
||||
@@ -613,6 +615,14 @@ class Config(object):
|
||||
self.hook.pytest_unconfigure(config=self)
|
||||
self.pluginmanager.ensure_shutdown()
|
||||
|
||||
def warn(self, code, message):
|
||||
""" generate a warning for this test session. """
|
||||
self.hook.pytest_logwarning(code=code, message=message,
|
||||
fslocation=None, nodeid=None)
|
||||
|
||||
def get_terminal_writer(self):
|
||||
return self.pluginmanager.getplugin("terminalreporter")._tw
|
||||
|
||||
def pytest_cmdline_parse(self, pluginmanager, args):
|
||||
assert self == pluginmanager.config, (self, pluginmanager.config)
|
||||
self.parse(args)
|
||||
@@ -670,12 +680,20 @@ class Config(object):
|
||||
plugins += self._conftest.getconftestmodules(fspath)
|
||||
return plugins
|
||||
|
||||
def pytest_load_initial_conftests(self, parser, args):
|
||||
self._conftest.setinitial(args)
|
||||
def pytest_load_initial_conftests(self, early_config):
|
||||
self._conftest.setinitial(early_config.known_args_namespace)
|
||||
pytest_load_initial_conftests.trylast = True
|
||||
|
||||
def _initini(self, args):
|
||||
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
|
||||
parsed_args = self._parser.parse_known_args(args)
|
||||
if parsed_args.inifilename:
|
||||
iniconfig = py.iniconfig.IniConfig(parsed_args.inifilename)
|
||||
if 'pytest' in iniconfig.sections:
|
||||
self.inicfg = iniconfig['pytest']
|
||||
else:
|
||||
self.inicfg = {}
|
||||
else:
|
||||
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
|
||||
self._parser.addini('addopts', 'extra command line options', 'args')
|
||||
self._parser.addini('minversion', 'minimally required pytest version')
|
||||
|
||||
@@ -687,8 +705,19 @@ class Config(object):
|
||||
self.pluginmanager.consider_preparse(args)
|
||||
self.pluginmanager.consider_setuptools_entrypoints()
|
||||
self.pluginmanager.consider_env()
|
||||
self.hook.pytest_load_initial_conftests(early_config=self,
|
||||
args=args, parser=self._parser)
|
||||
self.known_args_namespace = ns = self._parser.parse_known_args(args)
|
||||
try:
|
||||
self.hook.pytest_load_initial_conftests(early_config=self,
|
||||
args=args, parser=self._parser)
|
||||
except ConftestImportFailure:
|
||||
e = sys.exc_info()[1]
|
||||
if ns.help or ns.version:
|
||||
# we don't want to prevent --help/--version to work
|
||||
# so just let is pass and print a warning at the end
|
||||
self.pluginmanager._warnings.append(
|
||||
"could not load initial conftests (%s)\n" % e.path)
|
||||
else:
|
||||
raise
|
||||
|
||||
def _checkversion(self):
|
||||
import pytest
|
||||
@@ -704,14 +733,12 @@ class Config(object):
|
||||
|
||||
def parse(self, args):
|
||||
# parse given cmdline arguments into this config object.
|
||||
# Note that this can only be called once per testing process.
|
||||
assert not hasattr(self, 'args'), (
|
||||
"can only parse cmdline args at most once per Config object")
|
||||
self._origargs = args
|
||||
self._preparse(args)
|
||||
# XXX deprecated hook:
|
||||
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
||||
self._parser.hints.extend(self.pluginmanager._hints)
|
||||
args = self._parser.parse_setoption(args, self.option)
|
||||
if not args:
|
||||
args.append(py.std.os.getcwd())
|
||||
@@ -763,7 +790,7 @@ class Config(object):
|
||||
assert type is None
|
||||
return value
|
||||
|
||||
def _getconftest_pathlist(self, name, path=None):
|
||||
def _getconftest_pathlist(self, name, path):
|
||||
try:
|
||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||
except KeyError:
|
||||
@@ -777,47 +804,36 @@ class Config(object):
|
||||
l.append(relroot)
|
||||
return l
|
||||
|
||||
def _getconftest(self, name, path=None, check=False):
|
||||
if check:
|
||||
self._checkconftest(name)
|
||||
return self._conftest.rget(name, path)
|
||||
|
||||
def getoption(self, name):
|
||||
def getoption(self, name, default=notset, skip=False):
|
||||
""" return command line option value.
|
||||
|
||||
:arg name: name of the option. You may also specify
|
||||
the literal ``--OPT`` option instead of the "dest" option name.
|
||||
:arg default: default value if no option of that name exists.
|
||||
:arg skip: if True raise pytest.skip if option does not exists
|
||||
or has a None value.
|
||||
"""
|
||||
name = self._opt2dest.get(name, name)
|
||||
try:
|
||||
return getattr(self.option, name)
|
||||
val = getattr(self.option, name)
|
||||
if val is None and skip:
|
||||
raise AttributeError(name)
|
||||
return val
|
||||
except AttributeError:
|
||||
if default is not notset:
|
||||
return default
|
||||
if skip:
|
||||
import pytest
|
||||
pytest.skip("no %r option found" %(name,))
|
||||
raise ValueError("no option named %r" % (name,))
|
||||
|
||||
def getvalue(self, name, path=None):
|
||||
""" return command line option value.
|
||||
|
||||
:arg name: name of the command line option
|
||||
|
||||
(deprecated) if we can't find the option also lookup
|
||||
the name in a matching conftest file.
|
||||
"""
|
||||
try:
|
||||
return getattr(self.option, name)
|
||||
except AttributeError:
|
||||
return self._getconftest(name, path, check=False)
|
||||
""" (deprecated, use getoption()) """
|
||||
return self.getoption(name)
|
||||
|
||||
def getvalueorskip(self, name, path=None):
|
||||
""" (deprecated) return getvalue(name) or call
|
||||
py.test.skip if no value exists. """
|
||||
__tracebackhide__ = True
|
||||
try:
|
||||
val = self.getvalue(name, path)
|
||||
if val is None:
|
||||
raise KeyError(name)
|
||||
return val
|
||||
except KeyError:
|
||||
py.test.skip("no %r value found" %(name,))
|
||||
""" (deprecated, use getoption(skip=True)) """
|
||||
return self.getoption(name, skip=True)
|
||||
|
||||
def exists(path, ignore=EnvironmentError):
|
||||
try:
|
||||
@@ -841,6 +857,12 @@ def getcfg(args, inibasenames):
|
||||
return {}
|
||||
|
||||
|
||||
def node_with_line_number(string):
|
||||
split = string.split('[')
|
||||
split[0] = re.sub(r'@\d+', '', split[0])
|
||||
return '['.join(split)
|
||||
|
||||
|
||||
def setns(obj, dic):
|
||||
import pytest
|
||||
for name, value in dic.items():
|
||||
|
||||
120
_pytest/core.py
120
_pytest/core.py
@@ -4,6 +4,7 @@ pytest PluginManager, basic initialization and tracing.
|
||||
import sys
|
||||
import inspect
|
||||
import py
|
||||
# don't import pytest to avoid circular imports
|
||||
|
||||
assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
|
||||
"%s is too old, remove or upgrade 'py'" % (py.__version__))
|
||||
@@ -70,7 +71,7 @@ class PluginManager(object):
|
||||
self._name2plugin = {}
|
||||
self._listattrcache = {}
|
||||
self._plugins = []
|
||||
self._hints = []
|
||||
self._warnings = []
|
||||
self.trace = TagTracer().get("pluginmanage")
|
||||
self._plugin_distinfo = []
|
||||
self._shutdown = []
|
||||
@@ -136,7 +137,8 @@ class PluginManager(object):
|
||||
|
||||
def skipifmissing(self, name):
|
||||
if not self.hasplugin(name):
|
||||
py.test.skip("plugin %r is missing" % name)
|
||||
import pytest
|
||||
pytest.skip("plugin %r is missing" % name)
|
||||
|
||||
def hasplugin(self, name):
|
||||
return bool(self.getplugin(name))
|
||||
@@ -220,11 +222,10 @@ class PluginManager(object):
|
||||
raise
|
||||
except:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
if not hasattr(py.test, 'skip'):
|
||||
import pytest
|
||||
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
|
||||
raise
|
||||
elif not isinstance(e, py.test.skip.Exception):
|
||||
raise
|
||||
self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
|
||||
self._warnings.append("skipped plugin %r: %s" %((modname, e.msg)))
|
||||
else:
|
||||
self.register(mod, modname)
|
||||
self.consider_module(mod)
|
||||
@@ -239,18 +240,22 @@ class PluginManager(object):
|
||||
pass
|
||||
l = []
|
||||
last = []
|
||||
wrappers = []
|
||||
for plugin in plugins:
|
||||
try:
|
||||
meth = getattr(plugin, attrname)
|
||||
if hasattr(meth, 'tryfirst'):
|
||||
last.append(meth)
|
||||
elif hasattr(meth, 'trylast'):
|
||||
l.insert(0, meth)
|
||||
else:
|
||||
l.append(meth)
|
||||
except AttributeError:
|
||||
continue
|
||||
if hasattr(meth, 'hookwrapper'):
|
||||
wrappers.append(meth)
|
||||
elif hasattr(meth, 'tryfirst'):
|
||||
last.append(meth)
|
||||
elif hasattr(meth, 'trylast'):
|
||||
l.insert(0, meth)
|
||||
else:
|
||||
l.append(meth)
|
||||
l.extend(last)
|
||||
l.extend(wrappers)
|
||||
self._listattrcache[key] = list(l)
|
||||
return l
|
||||
|
||||
@@ -263,23 +268,22 @@ def importplugin(importspec):
|
||||
name = importspec
|
||||
try:
|
||||
mod = "_pytest." + name
|
||||
#print >>sys.stderr, "tryimport", mod
|
||||
__import__(mod)
|
||||
return sys.modules[mod]
|
||||
except ImportError:
|
||||
#e = py.std.sys.exc_info()[1]
|
||||
#if str(e).find(name) == -1:
|
||||
# raise
|
||||
pass #
|
||||
try:
|
||||
#print >>sys.stderr, "tryimport", importspec
|
||||
__import__(importspec)
|
||||
except ImportError:
|
||||
raise ImportError(importspec)
|
||||
return sys.modules[importspec]
|
||||
return sys.modules[importspec]
|
||||
|
||||
class MultiCall:
|
||||
""" execute a call into multiple python functions/methods. """
|
||||
|
||||
class WrongHookWrapper(Exception):
|
||||
""" a hook wrapper does not behave correctly. """
|
||||
def __init__(self, func, message):
|
||||
Exception.__init__(self, func, message)
|
||||
self.func = func
|
||||
self.message = message
|
||||
|
||||
def __init__(self, methods, kwargs, firstresult=False):
|
||||
self.methods = list(methods)
|
||||
self.kwargs = kwargs
|
||||
@@ -291,16 +295,39 @@ class MultiCall:
|
||||
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
|
||||
|
||||
def execute(self):
|
||||
while self.methods:
|
||||
method = self.methods.pop()
|
||||
kwargs = self.getkwargs(method)
|
||||
res = method(**kwargs)
|
||||
if res is not None:
|
||||
self.results.append(res)
|
||||
if self.firstresult:
|
||||
return res
|
||||
if not self.firstresult:
|
||||
return self.results
|
||||
next_finalizers = []
|
||||
try:
|
||||
while self.methods:
|
||||
method = self.methods.pop()
|
||||
kwargs = self.getkwargs(method)
|
||||
if hasattr(method, "hookwrapper"):
|
||||
it = method(**kwargs)
|
||||
next = getattr(it, "next", None)
|
||||
if next is None:
|
||||
next = getattr(it, "__next__", None)
|
||||
if next is None:
|
||||
raise self.WrongHookWrapper(method,
|
||||
"wrapper does not contain a yield")
|
||||
res = next()
|
||||
next_finalizers.append((method, next))
|
||||
else:
|
||||
res = method(**kwargs)
|
||||
if res is not None:
|
||||
self.results.append(res)
|
||||
if self.firstresult:
|
||||
return res
|
||||
if not self.firstresult:
|
||||
return self.results
|
||||
finally:
|
||||
for method, fin in reversed(next_finalizers):
|
||||
try:
|
||||
fin()
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
raise self.WrongHookWrapper(method,
|
||||
"wrapper contain more than one yield")
|
||||
|
||||
|
||||
def getkwargs(self, method):
|
||||
kwargs = {}
|
||||
@@ -313,19 +340,36 @@ class MultiCall:
|
||||
return kwargs
|
||||
|
||||
def varnames(func):
|
||||
""" return argument name tuple for a function, method, class or callable.
|
||||
|
||||
In case of a class, its "__init__" method is considered.
|
||||
For methods the "self" parameter is not included unless you are passing
|
||||
an unbound method with Python3 (which has no supports for unbound methods)
|
||||
"""
|
||||
cache = getattr(func, "__dict__", {})
|
||||
try:
|
||||
return func._varnames
|
||||
except AttributeError:
|
||||
return cache["_varnames"]
|
||||
except KeyError:
|
||||
pass
|
||||
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
||||
func = getattr(func, '__call__', func)
|
||||
ismethod = inspect.ismethod(func)
|
||||
if inspect.isclass(func):
|
||||
try:
|
||||
func = func.__init__
|
||||
except AttributeError:
|
||||
return ()
|
||||
ismethod = True
|
||||
else:
|
||||
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
||||
func = getattr(func, '__call__', func)
|
||||
ismethod = inspect.ismethod(func)
|
||||
rawcode = py.code.getrawcode(func)
|
||||
try:
|
||||
x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
|
||||
except AttributeError:
|
||||
x = ()
|
||||
py.builtin._getfuncdict(func)['_varnames'] = x
|
||||
try:
|
||||
cache["_varnames"] = x
|
||||
except TypeError:
|
||||
pass
|
||||
return x
|
||||
|
||||
class HookRelay:
|
||||
|
||||
@@ -91,8 +91,13 @@ class DoctestTextfile(DoctestItem, pytest.File):
|
||||
doctest = py.std.doctest
|
||||
# satisfy `FixtureRequest` constructor...
|
||||
self.funcargs = {}
|
||||
self._fixtureinfo = FuncFixtureInfo((), [], {})
|
||||
fm = self.session._fixturemanager
|
||||
def func():
|
||||
pass
|
||||
self._fixtureinfo = fm.getfixtureinfo(node=self, func=func,
|
||||
cls=None, funcargs=False)
|
||||
fixture_request = FixtureRequest(self)
|
||||
fixture_request._fillfixtures()
|
||||
failed, tot = doctest.testfile(
|
||||
str(self.fspath), module_relative=False,
|
||||
optionflags=doctest.ELLIPSIS,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
""" generate a single-file self-contained version of py.test """
|
||||
""" generate a single-file self-contained version of pytest """
|
||||
import py
|
||||
import sys
|
||||
|
||||
@@ -55,7 +55,7 @@ def pytest_addoption(parser):
|
||||
group = parser.getgroup("debugconfig")
|
||||
group.addoption("--genscript", action="store", default=None,
|
||||
dest="genscript", metavar="path",
|
||||
help="create standalone py.test script at given target path.")
|
||||
help="create standalone pytest script at given target path.")
|
||||
|
||||
def pytest_cmdline_main(config):
|
||||
genscript = config.getvalue("genscript")
|
||||
@@ -70,7 +70,7 @@ def pytest_cmdline_main(config):
|
||||
"or below due to 'argparse' dependency. Use python2.6 "
|
||||
"to generate a python2.5/6 compatible script", red=True)
|
||||
script = generate_script(
|
||||
'import py; raise SystemExit(py.test.cmdline.main())',
|
||||
'import pytest; raise SystemExit(pytest.cmdline.main())',
|
||||
deps,
|
||||
)
|
||||
genscript = py.path.local(genscript)
|
||||
|
||||
@@ -12,7 +12,9 @@ def pytest_addoption(parser):
|
||||
help="show help message and configuration info")
|
||||
group._addoption('-p', action="append", dest="plugins", default = [],
|
||||
metavar="name",
|
||||
help="early-load given plugin (multi-allowed).")
|
||||
help="early-load given plugin (multi-allowed). "
|
||||
"To avoid loading of plugins, use the `no:` prefix, e.g. "
|
||||
"`no:doctest`.")
|
||||
group.addoption('--traceconfig', '--trace-config',
|
||||
action="store_true", default=False,
|
||||
help="trace considerations of conftest.py files."),
|
||||
@@ -46,7 +48,7 @@ def pytest_unconfigure(config):
|
||||
def pytest_cmdline_main(config):
|
||||
if config.option.version:
|
||||
p = py.path.local(pytest.__file__)
|
||||
sys.stderr.write("This is py.test version %s, imported from %s\n" %
|
||||
sys.stderr.write("This is pytest version %s, imported from %s\n" %
|
||||
(pytest.__version__, p))
|
||||
plugininfo = getpluginversioninfo(config)
|
||||
if plugininfo:
|
||||
@@ -62,7 +64,6 @@ def pytest_cmdline_main(config):
|
||||
def showhelp(config):
|
||||
tw = py.io.TerminalWriter()
|
||||
tw.write(config._parser.optparser.format_help())
|
||||
tw.write(config._parser.optparser.format_epilog(None))
|
||||
tw.line()
|
||||
tw.line()
|
||||
#tw.sep( "=", "config file settings")
|
||||
@@ -82,16 +83,12 @@ def showhelp(config):
|
||||
#tw.sep("=")
|
||||
tw.line("to see available markers type: py.test --markers")
|
||||
tw.line("to see available fixtures type: py.test --fixtures")
|
||||
tw.line("(shown according to specified file_or_dir or current dir "
|
||||
"if not specified)")
|
||||
for warning in config.pluginmanager._warnings:
|
||||
tw.line("warning: %s" % (warning,), red=True)
|
||||
return
|
||||
|
||||
tw.line("conftest.py options:")
|
||||
tw.line()
|
||||
conftestitems = sorted(config._parser._conftestdict.items())
|
||||
for name, help in conftest_options + conftestitems:
|
||||
line = " %-15s %s" %(name, help)
|
||||
tw.line(line[:tw.fullwidth])
|
||||
tw.line()
|
||||
#tw.sep( "=")
|
||||
|
||||
conftest_options = [
|
||||
('pytest_plugins', 'list of plugin names to load'),
|
||||
@@ -120,7 +117,6 @@ def pytest_report_header(config):
|
||||
|
||||
if config.option.traceconfig:
|
||||
lines.append("active plugins:")
|
||||
plugins = []
|
||||
items = config.pluginmanager._name2plugin.items()
|
||||
for name, plugin in items:
|
||||
if hasattr(plugin, '__file__'):
|
||||
|
||||
@@ -11,8 +11,8 @@ def pytest_addhooks(pluginmanager):
|
||||
|
||||
def pytest_namespace():
|
||||
"""return dict of name->object to be made globally available in
|
||||
the py.test/pytest namespace. This hook is called before command
|
||||
line options are parsed.
|
||||
the pytest namespace. This hook is called before command line options
|
||||
are parsed.
|
||||
"""
|
||||
|
||||
def pytest_cmdline_parse(pluginmanager, args):
|
||||
@@ -53,8 +53,8 @@ def pytest_cmdline_main(config):
|
||||
pytest_cmdline_main.firstresult = True
|
||||
|
||||
def pytest_load_initial_conftests(args, early_config, parser):
|
||||
""" implements loading initial conftests.
|
||||
"""
|
||||
""" implements the loading of initial conftest files ahead
|
||||
of command line option parsing. """
|
||||
|
||||
def pytest_configure(config):
|
||||
""" called after command line options have been parsed
|
||||
@@ -152,9 +152,9 @@ def pytest_runtest_protocol(item, nextitem):
|
||||
|
||||
:arg item: test item for which the runtest protocol is performed.
|
||||
|
||||
:arg nexitem: the scheduled-to-be-next test item (or None if this
|
||||
is the end my friend). This argument is passed on to
|
||||
:py:func:`pytest_runtest_teardown`.
|
||||
:arg nextitem: the scheduled-to-be-next test item (or None if this
|
||||
is the end my friend). This argument is passed on to
|
||||
:py:func:`pytest_runtest_teardown`.
|
||||
|
||||
:return boolean: True if no further hook implementations should be invoked.
|
||||
"""
|
||||
@@ -172,10 +172,10 @@ def pytest_runtest_call(item):
|
||||
def pytest_runtest_teardown(item, nextitem):
|
||||
""" called after ``pytest_runtest_call``.
|
||||
|
||||
:arg nexitem: the scheduled-to-be-next test item (None if no further
|
||||
test item is scheduled). This argument can be used to
|
||||
perform exact teardowns, i.e. calling just enough finalizers
|
||||
so that nextitem only needs to call setup-functions.
|
||||
:arg nextitem: the scheduled-to-be-next test item (None if no further
|
||||
test item is scheduled). This argument can be used to
|
||||
perform exact teardowns, i.e. calling just enough finalizers
|
||||
so that nextitem only needs to call setup-functions.
|
||||
"""
|
||||
|
||||
def pytest_runtest_makereport(item, call):
|
||||
@@ -227,6 +227,11 @@ pytest_report_teststatus.firstresult = True
|
||||
def pytest_terminal_summary(terminalreporter):
|
||||
""" add additional section in terminal summary reporting. """
|
||||
|
||||
def pytest_logwarning(message, code, nodeid, fslocation):
|
||||
""" process a warning specified by a message, a code string,
|
||||
a nodeid and fslocation (both of which may be None
|
||||
if the warning is not tied to a partilar node/location)."""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# doctest hooks
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -9,7 +9,6 @@ import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
# Python 2.X and 3.X compatibility
|
||||
try:
|
||||
unichr(65)
|
||||
@@ -109,12 +108,14 @@ class LogXML(object):
|
||||
))
|
||||
|
||||
def _write_captured_output(self, report):
|
||||
sec = dict(report.sections)
|
||||
for name in ('out', 'err'):
|
||||
content = sec.get("Captured std%s" % name)
|
||||
if content:
|
||||
tag = getattr(Junit, 'system-'+name)
|
||||
self.append(tag(bin_xml_escape(content)))
|
||||
for capname in ('out', 'err'):
|
||||
allcontent = ""
|
||||
for name, content in report.get_sections("Captured std%s" %
|
||||
capname):
|
||||
allcontent += content
|
||||
if allcontent:
|
||||
tag = getattr(Junit, 'system-'+capname)
|
||||
self.append(tag(bin_xml_escape(allcontent)))
|
||||
|
||||
def append(self, obj):
|
||||
self.tests[-1].append(obj)
|
||||
@@ -131,38 +132,38 @@ class LogXML(object):
|
||||
self.skipped += 1
|
||||
else:
|
||||
fail = Junit.failure(message="test failure")
|
||||
fail.append(str(report.longrepr))
|
||||
fail.append(bin_xml_escape(report.longrepr))
|
||||
self.append(fail)
|
||||
self.failed += 1
|
||||
self._write_captured_output(report)
|
||||
|
||||
def append_collect_failure(self, report):
|
||||
#msg = str(report.longrepr.reprtraceback.extraline)
|
||||
self.append(Junit.failure(str(report.longrepr),
|
||||
self.append(Junit.failure(bin_xml_escape(report.longrepr),
|
||||
message="collection failure"))
|
||||
self.errors += 1
|
||||
|
||||
def append_collect_skipped(self, report):
|
||||
#msg = str(report.longrepr.reprtraceback.extraline)
|
||||
self.append(Junit.skipped(str(report.longrepr),
|
||||
self.append(Junit.skipped(bin_xml_escape(report.longrepr),
|
||||
message="collection skipped"))
|
||||
self.skipped += 1
|
||||
|
||||
def append_error(self, report):
|
||||
self.append(Junit.error(str(report.longrepr),
|
||||
self.append(Junit.error(bin_xml_escape(report.longrepr),
|
||||
message="test setup failure"))
|
||||
self.errors += 1
|
||||
|
||||
def append_skipped(self, report):
|
||||
if hasattr(report, "wasxfail"):
|
||||
self.append(Junit.skipped(str(report.wasxfail),
|
||||
self.append(Junit.skipped(bin_xml_escape(report.wasxfail),
|
||||
message="expected test failure"))
|
||||
else:
|
||||
filename, lineno, skipreason = report.longrepr
|
||||
if skipreason.startswith("Skipped: "):
|
||||
skipreason = skipreason[9:]
|
||||
skipreason = bin_xml_escape(skipreason[9:])
|
||||
self.append(
|
||||
Junit.skipped("%s:%s: %s" % report.longrepr,
|
||||
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
|
||||
type="pytest.skip",
|
||||
message=skipreason
|
||||
))
|
||||
@@ -194,17 +195,17 @@ class LogXML(object):
|
||||
|
||||
def pytest_internalerror(self, excrepr):
|
||||
self.errors += 1
|
||||
data = py.xml.escape(excrepr)
|
||||
data = bin_xml_escape(excrepr)
|
||||
self.tests.append(
|
||||
Junit.testcase(
|
||||
Junit.error(data, message="internal error"),
|
||||
classname="pytest",
|
||||
name="internal"))
|
||||
|
||||
def pytest_sessionstart(self, session):
|
||||
def pytest_sessionstart(self):
|
||||
self.suite_start_time = time.time()
|
||||
|
||||
def pytest_sessionfinish(self, session, exitstatus, __multicall__):
|
||||
def pytest_sessionfinish(self):
|
||||
if py.std.sys.version_info[0] < 3:
|
||||
logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8')
|
||||
else:
|
||||
|
||||
@@ -8,7 +8,7 @@ try:
|
||||
except ImportError:
|
||||
from UserDict import DictMixin as MappingMixin
|
||||
|
||||
from _pytest.runner import collect_one_node, Skipped
|
||||
from _pytest.runner import collect_one_node
|
||||
|
||||
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
|
||||
|
||||
@@ -23,7 +23,7 @@ name_re = py.std.re.compile("^[a-zA-Z_]\w*$")
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
|
||||
type="args", default=('.*', 'CVS', '_darcs', '{arch}'))
|
||||
type="args", default=('.*', 'CVS', '_darcs', '{arch}', '*.egg'))
|
||||
#parser.addini("dirpatterns",
|
||||
# "patterns specifying possible locations of test files",
|
||||
# type="linelist", default=["**/test_*.txt",
|
||||
@@ -38,6 +38,8 @@ def pytest_addoption(parser):
|
||||
help="exit after first num failures or errors.")
|
||||
group._addoption('--strict', action="store_true",
|
||||
help="run pytest in strict mode, warnings become errors.")
|
||||
group._addoption("-c", metavar="file", type=str, dest="inifilename",
|
||||
help="load configuration from `file` instead of trying to locate one of the implicit configuration files.")
|
||||
|
||||
group = parser.getgroup("collect", "collection")
|
||||
group.addoption('--collectonly', '--collect-only', action="store_true",
|
||||
@@ -63,7 +65,7 @@ def pytest_namespace():
|
||||
return dict(collect=collect)
|
||||
|
||||
def pytest_configure(config):
|
||||
py.test.config = config # compatibiltiy
|
||||
pytest.config = config # compatibiltiy
|
||||
if config.option.exitfirst:
|
||||
config.option.maxfail = 1
|
||||
|
||||
@@ -80,8 +82,9 @@ def wrap_session(config, doit):
|
||||
initstate = 2
|
||||
doit(config, session)
|
||||
except pytest.UsageError:
|
||||
msg = sys.exc_info()[1].args[0]
|
||||
sys.stderr.write("ERROR: %s\n" %(msg,))
|
||||
args = sys.exc_info()[1].args
|
||||
for msg in args:
|
||||
sys.stderr.write("ERROR: %s\n" %(msg,))
|
||||
session.exitstatus = EXIT_USAGEERROR
|
||||
except KeyboardInterrupt:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
@@ -97,6 +100,7 @@ def wrap_session(config, doit):
|
||||
if session._testsfailed:
|
||||
session.exitstatus = EXIT_TESTSFAILED
|
||||
finally:
|
||||
excinfo = None # Explicitly break reference cycle.
|
||||
session.startdir.chdir()
|
||||
if initstate >= 2:
|
||||
config.hook.pytest_sessionfinish(
|
||||
@@ -143,7 +147,7 @@ def pytest_ignore_collect(path, config):
|
||||
p = path.dirpath()
|
||||
ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
|
||||
ignore_paths = ignore_paths or []
|
||||
excludeopt = config.getvalue("ignore")
|
||||
excludeopt = config.getoption("ignore")
|
||||
if excludeopt:
|
||||
ignore_paths.extend([py.path.local(x) for x in excludeopt])
|
||||
return path in ignore_paths
|
||||
@@ -230,6 +234,9 @@ class Node(object):
|
||||
#: allow adding of extra keywords to use for matching
|
||||
self.extra_keyword_matches = set()
|
||||
|
||||
# used for storing artificial fixturedefs for direct parametrization
|
||||
self._name2pseudofixturedef = {}
|
||||
|
||||
#self.extrainit()
|
||||
|
||||
@property
|
||||
@@ -260,6 +267,20 @@ class Node(object):
|
||||
return "<%s %r>" %(self.__class__.__name__,
|
||||
getattr(self, 'name', None))
|
||||
|
||||
def warn(self, code, message):
|
||||
""" generate a warning with the given code and message for this
|
||||
item. """
|
||||
assert isinstance(code, str)
|
||||
fslocation = getattr(self, "location", None)
|
||||
if fslocation is None:
|
||||
fslocation = getattr(self, "fspath", None)
|
||||
else:
|
||||
fslocation = "%s:%s" % fslocation[:2]
|
||||
|
||||
self.ihook.pytest_logwarning(code=code, message=message,
|
||||
nodeid=self.nodeid,
|
||||
fslocation=fslocation)
|
||||
|
||||
# methods for ordering nodes
|
||||
@property
|
||||
def nodeid(self):
|
||||
@@ -270,21 +291,11 @@ class Node(object):
|
||||
self._nodeid = x = self._makeid()
|
||||
return x
|
||||
|
||||
|
||||
def _makeid(self):
|
||||
return self.parent.nodeid + "::" + self.name
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Node):
|
||||
return False
|
||||
return (self.__class__ == other.__class__ and
|
||||
self.name == other.name and self.parent == other.parent)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.name, self.parent))
|
||||
return hash(self.nodeid)
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
@@ -365,6 +376,8 @@ class Node(object):
|
||||
self.session._setupstate.addfinalizer(fin, self)
|
||||
|
||||
def getparent(self, cls):
|
||||
""" get the next parent node (including ourself)
|
||||
which is an instance of the given class"""
|
||||
current = self
|
||||
while current and not isinstance(current, cls):
|
||||
current = current.parent
|
||||
@@ -377,20 +390,24 @@ class Node(object):
|
||||
fm = self.session._fixturemanager
|
||||
if excinfo.errisinstance(fm.FixtureLookupError):
|
||||
return excinfo.value.formatrepr()
|
||||
tbfilter = True
|
||||
if self.config.option.fulltrace:
|
||||
style="long"
|
||||
else:
|
||||
self._prunetraceback(excinfo)
|
||||
# XXX should excinfo.getrepr record all data and toterminal()
|
||||
# process it?
|
||||
tbfilter = False # prunetraceback already does it
|
||||
if style == "auto":
|
||||
style = "long"
|
||||
# XXX should excinfo.getrepr record all data and toterminal() process it?
|
||||
if style is None:
|
||||
if self.config.option.tbstyle == "short":
|
||||
style = "short"
|
||||
else:
|
||||
style = "long"
|
||||
|
||||
return excinfo.getrepr(funcargs=True,
|
||||
showlocals=self.config.option.showlocals,
|
||||
style=style)
|
||||
style=style, tbfilter=tbfilter)
|
||||
|
||||
repr_failure = _repr_failure_py
|
||||
|
||||
@@ -399,10 +416,6 @@ class Collector(Node):
|
||||
and thus iteratively build a tree.
|
||||
"""
|
||||
|
||||
# the set of exceptions to interpret as "Skip the whole module" during
|
||||
# collection
|
||||
skip_exceptions = (Skipped,)
|
||||
|
||||
class CollectError(Exception):
|
||||
""" an error during collection, contains a custom message. """
|
||||
|
||||
@@ -425,7 +438,6 @@ class Collector(Node):
|
||||
|
||||
def _prunetraceback(self, excinfo):
|
||||
if hasattr(self, 'fspath'):
|
||||
path = self.fspath
|
||||
traceback = excinfo.traceback
|
||||
ntraceback = traceback.cut(path=self.fspath)
|
||||
if ntraceback == traceback:
|
||||
@@ -461,6 +473,14 @@ class Item(Node):
|
||||
"""
|
||||
nextitem = None
|
||||
|
||||
def __init__(self, name, parent=None, config=None, session=None):
|
||||
super(Item, self).__init__(name, parent, config, session)
|
||||
self._report_sections = []
|
||||
|
||||
def add_report_section(self, when, key, content):
|
||||
if content:
|
||||
self._report_sections.append((when, key, content))
|
||||
|
||||
def reportinfo(self):
|
||||
return self.fspath, None, ""
|
||||
|
||||
@@ -545,9 +565,12 @@ class Session(FSCollector):
|
||||
self.ihook.pytest_collectreport(report=rep)
|
||||
self.trace.root.indent -= 1
|
||||
if self._notfound:
|
||||
errors = []
|
||||
for arg, exc in self._notfound:
|
||||
line = "(no name %r in any of %r)" % (arg, exc.args[0])
|
||||
raise pytest.UsageError("not found: %s\n%s" %(arg, line))
|
||||
errors.append("not found: %s\n%s" % (arg, line))
|
||||
#XXX: test this
|
||||
raise pytest.UsageError(*errors)
|
||||
if not genitems:
|
||||
return rep.result
|
||||
else:
|
||||
@@ -568,8 +591,7 @@ class Session(FSCollector):
|
||||
# we are inside a make_report hook so
|
||||
# we cannot directly pass through the exception
|
||||
self._notfound.append((arg, sys.exc_info()[1]))
|
||||
self.trace.root.indent -= 1
|
||||
break
|
||||
|
||||
self.trace.root.indent -= 1
|
||||
|
||||
def _collect(self, arg):
|
||||
@@ -698,11 +720,4 @@ class Session(FSCollector):
|
||||
yield x
|
||||
node.ihook.pytest_collectreport(report=rep)
|
||||
|
||||
def getfslineno(obj):
|
||||
# xxx let decorators etc specify a sane ordering
|
||||
if hasattr(obj, 'place_as'):
|
||||
obj = obj.place_as
|
||||
fslineno = py.code.getfslineno(obj)
|
||||
assert isinstance(fslineno[1], int), obj
|
||||
return fslineno
|
||||
|
||||
|
||||
@@ -140,7 +140,13 @@ def matchkeyword(colitem, keywordexpr):
|
||||
for name in colitem.function.__dict__:
|
||||
mapped_names.add(name)
|
||||
|
||||
return eval(keywordexpr, {}, KeywordMapping(mapped_names))
|
||||
mapping = KeywordMapping(mapped_names)
|
||||
if " " not in keywordexpr:
|
||||
# special case to allow for simple "-k pass" and "-k 1.3"
|
||||
return mapping[keywordexpr]
|
||||
elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
|
||||
return not mapping[keywordexpr[4:]]
|
||||
return eval(keywordexpr, {}, mapping)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
@@ -151,10 +157,10 @@ def pytest_configure(config):
|
||||
|
||||
class MarkGenerator:
|
||||
""" Factory for :class:`MarkDecorator` objects - exposed as
|
||||
a ``py.test.mark`` singleton instance. Example::
|
||||
a ``pytest.mark`` singleton instance. Example::
|
||||
|
||||
import py
|
||||
@py.test.mark.slowtest
|
||||
@pytest.mark.slowtest
|
||||
def test_function():
|
||||
pass
|
||||
|
||||
@@ -163,7 +169,7 @@ class MarkGenerator:
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name[0] == "_":
|
||||
raise AttributeError(name)
|
||||
raise AttributeError("Marker name must NOT start with underscore")
|
||||
if hasattr(self, '_config'):
|
||||
self._check(name)
|
||||
return MarkDecorator(name)
|
||||
@@ -182,6 +188,9 @@ class MarkGenerator:
|
||||
if name not in self._markers:
|
||||
raise AttributeError("%r not a registered marker" % (name,))
|
||||
|
||||
def istestfunc(func):
|
||||
return hasattr(func, "__call__") and \
|
||||
getattr(func, "__name__", "<lambda>") != "<lambda>"
|
||||
|
||||
class MarkDecorator:
|
||||
""" A decorator for test functions and test classes. When applied
|
||||
@@ -189,14 +198,32 @@ class MarkDecorator:
|
||||
:ref:`retrieved by hooks as item keywords <excontrolskip>`.
|
||||
MarkDecorator instances are often created like this::
|
||||
|
||||
mark1 = py.test.mark.NAME # simple MarkDecorator
|
||||
mark2 = py.test.mark.NAME(name1=value) # parametrized MarkDecorator
|
||||
mark1 = pytest.mark.NAME # simple MarkDecorator
|
||||
mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
|
||||
|
||||
and can then be applied as decorators to test functions::
|
||||
|
||||
@mark2
|
||||
def test_function():
|
||||
pass
|
||||
|
||||
When a MarkDecorator instance is called it does the following:
|
||||
1. If called with a single class as its only positional argument and no
|
||||
additional keyword arguments, it attaches itself to the class so it
|
||||
gets applied automatically to all test cases found in that class.
|
||||
2. If called with a single function as its only positional argument and
|
||||
no additional keyword arguments, it attaches a MarkInfo object to the
|
||||
function, containing all the arguments already stored internally in
|
||||
the MarkDecorator.
|
||||
3. When called in any other case, it performs a 'fake construction' call,
|
||||
i.e. it returns a new MarkDecorator instance with the original
|
||||
MarkDecorator's content updated with the arguments passed to this
|
||||
call.
|
||||
|
||||
Note: The rules above prevent MarkDecorator objects from storing only a
|
||||
single function or class reference as their positional argument with no
|
||||
additional keyword or positional arguments.
|
||||
|
||||
"""
|
||||
def __init__(self, name, args=None, kwargs=None):
|
||||
self.name = name
|
||||
@@ -215,10 +242,10 @@ class MarkDecorator:
|
||||
def __call__(self, *args, **kwargs):
|
||||
""" if passed a single callable argument: decorate it with mark info.
|
||||
otherwise add *args/**kwargs in-place to mark information. """
|
||||
if args:
|
||||
if args and not kwargs:
|
||||
func = args[0]
|
||||
if len(args) == 1 and hasattr(func, '__call__') or \
|
||||
hasattr(func, '__bases__'):
|
||||
if len(args) == 1 and (istestfunc(func) or
|
||||
hasattr(func, '__bases__')):
|
||||
if hasattr(func, '__bases__'):
|
||||
if hasattr(func, 'pytestmark'):
|
||||
l = func.pytestmark
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
""" monkeypatching and mocking functionality. """
|
||||
|
||||
import os, sys
|
||||
from py.builtin import _basestring
|
||||
|
||||
def pytest_funcarg__monkeypatch(request):
|
||||
"""The returned ``monkeypatch`` funcarg provides these
|
||||
@@ -28,7 +29,7 @@ def pytest_funcarg__monkeypatch(request):
|
||||
|
||||
def derive_importpath(import_path):
|
||||
import pytest
|
||||
if not isinstance(import_path, str) or "." not in import_path:
|
||||
if not isinstance(import_path, _basestring) or "." not in import_path:
|
||||
raise TypeError("must be absolute import path string, not %r" %
|
||||
(import_path,))
|
||||
rest = []
|
||||
@@ -58,7 +59,11 @@ def derive_importpath(import_path):
|
||||
|
||||
|
||||
|
||||
notset = object()
|
||||
class Notset:
|
||||
def __repr__(self):
|
||||
return "<notset>"
|
||||
|
||||
notset = Notset()
|
||||
|
||||
class monkeypatch:
|
||||
""" object keeping a record of setattr/item/env/syspath changes. """
|
||||
@@ -85,7 +90,7 @@ class monkeypatch:
|
||||
import inspect
|
||||
|
||||
if value is notset:
|
||||
if not isinstance(target, str):
|
||||
if not isinstance(target, _basestring):
|
||||
raise TypeError("use setattr(target, name, value) or "
|
||||
"setattr(target, value) with target being a dotted "
|
||||
"import string")
|
||||
@@ -115,7 +120,7 @@ class monkeypatch:
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
if name is notset:
|
||||
if not isinstance(target, str):
|
||||
if not isinstance(target, _basestring):
|
||||
raise TypeError("use delattr(target, name) or "
|
||||
"delattr(target) with target being a dotted "
|
||||
"import string")
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
""" run test suites written for nose. """
|
||||
|
||||
import pytest, py
|
||||
import sys
|
||||
|
||||
import py
|
||||
import pytest
|
||||
from _pytest import unittest
|
||||
|
||||
|
||||
def get_skip_exceptions():
|
||||
skip_classes = set()
|
||||
for module_name in ('unittest', 'unittest2', 'nose'):
|
||||
mod = sys.modules.get(module_name)
|
||||
if hasattr(mod, 'SkipTest'):
|
||||
skip_classes.add(mod.SkipTest)
|
||||
return tuple(skip_classes)
|
||||
|
||||
|
||||
def pytest_runtest_makereport(__multicall__, item, call):
|
||||
SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
|
||||
if SkipTest:
|
||||
if call.excinfo and call.excinfo.errisinstance(SkipTest):
|
||||
# let's substitute the excinfo with a py.test.skip one
|
||||
call2 = call.__class__(lambda:
|
||||
pytest.skip(str(call.excinfo.value)), call.when)
|
||||
call.excinfo = call2.excinfo
|
||||
if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
|
||||
# let's substitute the excinfo with a pytest.skip one
|
||||
call2 = call.__class__(lambda:
|
||||
pytest.skip(str(call.excinfo.value)), call.when)
|
||||
call.excinfo = call2.excinfo
|
||||
|
||||
|
||||
@pytest.mark.trylast
|
||||
@@ -38,13 +48,8 @@ def teardown_nose(item):
|
||||
# #call_optional(item._nosegensetup, 'teardown')
|
||||
# del item.parent._nosegensetup
|
||||
|
||||
|
||||
def pytest_make_collect_report(collector):
|
||||
SkipTest = getattr(sys.modules.get('unittest', None), 'SkipTest', None)
|
||||
if SkipTest is not None:
|
||||
collector.skip_exceptions += (SkipTest,)
|
||||
SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
|
||||
if SkipTest is not None:
|
||||
collector.skip_exceptions += (SkipTest,)
|
||||
if isinstance(collector, pytest.Generator):
|
||||
call_optional(collector.obj, 'setup')
|
||||
|
||||
|
||||
@@ -16,50 +16,37 @@ def pytest_configure(config):
|
||||
if config.getvalue("usepdb"):
|
||||
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
|
||||
|
||||
old_trace = py.std.pdb.set_trace
|
||||
old = (py.std.pdb.set_trace, pytestPDB._pluginmanager)
|
||||
def fin():
|
||||
py.std.pdb.set_trace = old_trace
|
||||
py.std.pdb.set_trace, pytestPDB._pluginmanager = old
|
||||
py.std.pdb.set_trace = pytest.set_trace
|
||||
pytestPDB._pluginmanager = config.pluginmanager
|
||||
config._cleanup.append(fin)
|
||||
|
||||
class pytestPDB:
|
||||
""" Pseudo PDB that defers to the real pdb. """
|
||||
item = None
|
||||
collector = None
|
||||
_pluginmanager = None
|
||||
|
||||
def set_trace(self):
|
||||
""" invoke PDB set_trace debugging, dropping any IO capturing. """
|
||||
frame = sys._getframe().f_back
|
||||
item = self.item or self.collector
|
||||
|
||||
if item is not None:
|
||||
capman = item.config.pluginmanager.getplugin("capturemanager")
|
||||
out, err = capman.suspendcapture()
|
||||
if hasattr(item, 'outerr'):
|
||||
item.outerr = (item.outerr[0] + out, item.outerr[1] + err)
|
||||
capman = None
|
||||
if self._pluginmanager is not None:
|
||||
capman = self._pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
capman.suspendcapture(in_=True)
|
||||
tw = py.io.TerminalWriter()
|
||||
tw.line()
|
||||
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
||||
py.std.pdb.Pdb().set_trace(frame)
|
||||
|
||||
def pdbitem(item):
|
||||
pytestPDB.item = item
|
||||
pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_make_collect_report(__multicall__, collector):
|
||||
try:
|
||||
pytestPDB.collector = collector
|
||||
return __multicall__.execute()
|
||||
finally:
|
||||
pytestPDB.collector = None
|
||||
|
||||
def pytest_runtest_makereport():
|
||||
pytestPDB.item = None
|
||||
|
||||
class PdbInvoke:
|
||||
def pytest_exception_interact(self, node, call, report):
|
||||
return _enter_pdb(node, call.excinfo, report)
|
||||
capman = node.config.pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
capman.suspendcapture(in_=True)
|
||||
_enter_pdb(node, call.excinfo, report)
|
||||
|
||||
def pytest_internalerror(self, excrepr, excinfo):
|
||||
for line in str(excrepr).split("\n"):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
""" (disabled by default) support for testing py.test and py.test plugins. """
|
||||
""" (disabled by default) support for testing pytest and pytest plugins. """
|
||||
|
||||
import py, pytest
|
||||
import sys, os
|
||||
@@ -26,7 +26,6 @@ def pytest_addoption(parser):
|
||||
def pytest_configure(config):
|
||||
# This might be called multiple times. Only take the first.
|
||||
global _pytest_fullpath
|
||||
import pytest
|
||||
try:
|
||||
_pytest_fullpath
|
||||
except NameError:
|
||||
@@ -121,7 +120,6 @@ class HookRecorder:
|
||||
|
||||
def contains(self, entries):
|
||||
__tracebackhide__ = True
|
||||
from py.builtin import print_
|
||||
i = 0
|
||||
entries = list(entries)
|
||||
backlocals = py.std.sys._getframe(1).f_locals
|
||||
@@ -139,7 +137,7 @@ class HookRecorder:
|
||||
break
|
||||
print_("NONAMEMATCH", name, "with", call)
|
||||
else:
|
||||
py.test.fail("could not find %r check %r" % (name, check))
|
||||
pytest.fail("could not find %r check %r" % (name, check))
|
||||
|
||||
def popcall(self, name):
|
||||
__tracebackhide__ = True
|
||||
@@ -149,7 +147,7 @@ class HookRecorder:
|
||||
return call
|
||||
lines = ["could not find call %r, in:" % (name,)]
|
||||
lines.extend([" %s" % str(x) for x in self.calls])
|
||||
py.test.fail("\n".join(lines))
|
||||
pytest.fail("\n".join(lines))
|
||||
|
||||
def getcall(self, name):
|
||||
l = self.getcalls(name)
|
||||
@@ -248,8 +246,14 @@ class TmpTestdir:
|
||||
ret = None
|
||||
for name, value in items:
|
||||
p = self.tmpdir.join(name).new(ext=ext)
|
||||
source = py.builtin._totext(py.code.Source(value)).strip()
|
||||
content = source.encode("utf-8") # + "\n"
|
||||
source = py.code.Source(value)
|
||||
def my_totext(s, encoding="utf-8"):
|
||||
if py.builtin._isbytes(s):
|
||||
s = py.builtin._totext(s, encoding=encoding)
|
||||
return s
|
||||
source_unicode = "\n".join([my_totext(line) for line in source.lines])
|
||||
source = py.builtin._totext(source_unicode)
|
||||
content = source.strip().encode("utf-8") # + "\n"
|
||||
#content = content.rstrip() + "\n"
|
||||
p.write(content, "wb")
|
||||
if ret is None:
|
||||
@@ -260,9 +264,6 @@ class TmpTestdir:
|
||||
def makefile(self, ext, *args, **kwargs):
|
||||
return self._makefile(ext, args, kwargs)
|
||||
|
||||
def makeini(self, source):
|
||||
return self.makefile('cfg', setup=source)
|
||||
|
||||
def makeconftest(self, source):
|
||||
return self.makepyfile(conftest=source)
|
||||
|
||||
@@ -472,12 +473,12 @@ class TmpTestdir:
|
||||
|
||||
def _getpybinargs(self, scriptname):
|
||||
if not self.request.config.getvalue("notoolsonpath"):
|
||||
# XXX we rely on script refering to the correct environment
|
||||
# XXX we rely on script referring to the correct environment
|
||||
# we cannot use "(py.std.sys.executable,script)"
|
||||
# becaue on windows the script is e.g. a py.test.exe
|
||||
return (py.std.sys.executable, _pytest_fullpath,)
|
||||
# because on windows the script is e.g. a py.test.exe
|
||||
return (py.std.sys.executable, _pytest_fullpath,) # noqa
|
||||
else:
|
||||
py.test.skip("cannot run %r with --no-tools-on-path" % scriptname)
|
||||
pytest.skip("cannot run %r with --no-tools-on-path" % scriptname)
|
||||
|
||||
def runpython(self, script, prepend=True):
|
||||
if prepend:
|
||||
@@ -514,22 +515,23 @@ class TmpTestdir:
|
||||
|
||||
def spawn_pytest(self, string, expect_timeout=10.0):
|
||||
if self.request.config.getvalue("notoolsonpath"):
|
||||
py.test.skip("--no-tools-on-path prevents running pexpect-spawn tests")
|
||||
pytest.skip("--no-tools-on-path prevents running pexpect-spawn tests")
|
||||
basetemp = self.tmpdir.mkdir("pexpect")
|
||||
invoke = " ".join(map(str, self._getpybinargs("py.test")))
|
||||
cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
|
||||
return self.spawn(cmd, expect_timeout=expect_timeout)
|
||||
|
||||
def spawn(self, cmd, expect_timeout=10.0):
|
||||
pexpect = py.test.importorskip("pexpect", "2.4")
|
||||
pexpect = pytest.importorskip("pexpect", "3.0")
|
||||
if hasattr(sys, 'pypy_version_info') and '64' in py.std.platform.machine():
|
||||
pytest.skip("pypy-64 bit not supported")
|
||||
if sys.platform == "darwin":
|
||||
pytest.xfail("pexpect does not work reliably on darwin?!")
|
||||
if sys.platform.startswith("freebsd"):
|
||||
pytest.xfail("pexpect does not work reliably on freebsd")
|
||||
logfile = self.tmpdir.join("spawn.out")
|
||||
child = pexpect.spawn(cmd, logfile=logfile.open("w"))
|
||||
logfile = self.tmpdir.join("spawn.out").open("wb")
|
||||
child = pexpect.spawn(cmd, logfile=logfile)
|
||||
self.request.addfinalizer(logfile.close)
|
||||
child.timeout = expect_timeout
|
||||
return child
|
||||
|
||||
@@ -692,4 +694,4 @@ class LineMatcher:
|
||||
show(" and:", repr(nextline))
|
||||
extralines.append(nextline)
|
||||
else:
|
||||
py.test.fail("remains unmatched: %r, see stderr" % (line,))
|
||||
pytest.fail("remains unmatched: %r, see stderr" % (line,))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ import py
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("terminal reporting", "resultlog plugin options")
|
||||
group.addoption('--resultlog', '--result-log', action="store",
|
||||
group.addoption('--resultlog', '--result-log', action="store",
|
||||
metavar="path", default=None,
|
||||
help="path for machine-readable result log.")
|
||||
|
||||
@@ -85,7 +85,7 @@ class ResultLog(object):
|
||||
if not report.passed:
|
||||
if report.failed:
|
||||
code = "F"
|
||||
longrepr = str(report.longrepr.reprcrash)
|
||||
longrepr = str(report.longrepr)
|
||||
else:
|
||||
assert report.skipped
|
||||
code = "S"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
""" basic collect and runtest protocol implementations """
|
||||
|
||||
import py, sys
|
||||
import py
|
||||
import pytest
|
||||
import sys
|
||||
from time import time
|
||||
from py._code.code import TerminalRepr
|
||||
|
||||
@@ -133,14 +135,13 @@ class CallInfo:
|
||||
self.when = when
|
||||
self.start = time()
|
||||
try:
|
||||
try:
|
||||
self.result = func()
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
self.excinfo = py.code.ExceptionInfo()
|
||||
finally:
|
||||
self.result = func()
|
||||
except KeyboardInterrupt:
|
||||
self.stop = time()
|
||||
raise
|
||||
except:
|
||||
self.excinfo = py.code.ExceptionInfo()
|
||||
self.stop = time()
|
||||
|
||||
def __repr__(self):
|
||||
if self.excinfo:
|
||||
@@ -176,6 +177,11 @@ class BaseReport(object):
|
||||
except UnicodeEncodeError:
|
||||
out.line("<unprintable longrepr>")
|
||||
|
||||
def get_sections(self, prefix):
|
||||
for name, content in self.sections:
|
||||
if name.startswith(prefix):
|
||||
yield prefix, content
|
||||
|
||||
passed = property(lambda x: x.outcome == "passed")
|
||||
failed = property(lambda x: x.outcome == "failed")
|
||||
skipped = property(lambda x: x.outcome == "skipped")
|
||||
@@ -189,15 +195,15 @@ def pytest_runtest_makereport(item, call):
|
||||
duration = call.stop-call.start
|
||||
keywords = dict([(x,1) for x in item.keywords])
|
||||
excinfo = call.excinfo
|
||||
sections = []
|
||||
if not call.excinfo:
|
||||
outcome = "passed"
|
||||
longrepr = None
|
||||
else:
|
||||
excinfo = call.excinfo
|
||||
if not isinstance(excinfo, py.code.ExceptionInfo):
|
||||
outcome = "failed"
|
||||
longrepr = excinfo
|
||||
elif excinfo.errisinstance(py.test.skip.Exception):
|
||||
elif excinfo.errisinstance(pytest.skip.Exception):
|
||||
outcome = "skipped"
|
||||
r = excinfo._getreprcrash()
|
||||
longrepr = (str(r.path), r.lineno, r.message)
|
||||
@@ -208,16 +214,18 @@ def pytest_runtest_makereport(item, call):
|
||||
else: # exception in setup or teardown
|
||||
longrepr = item._repr_failure_py(excinfo,
|
||||
style=item.config.option.tbstyle)
|
||||
for rwhen, key, content in item._report_sections:
|
||||
sections.append(("Captured std%s %s" %(key, rwhen), content))
|
||||
return TestReport(item.nodeid, item.location,
|
||||
keywords, outcome, longrepr, when,
|
||||
duration=duration)
|
||||
sections, duration)
|
||||
|
||||
class TestReport(BaseReport):
|
||||
""" Basic test report object (also used for setup and teardown calls if
|
||||
they fail).
|
||||
"""
|
||||
def __init__(self, nodeid, location,
|
||||
keywords, outcome, longrepr, when, sections=(), duration=0, **extra):
|
||||
def __init__(self, nodeid, location, keywords, outcome,
|
||||
longrepr, when, sections=(), duration=0, **extra):
|
||||
#: normalized collection node id
|
||||
self.nodeid = nodeid
|
||||
|
||||
@@ -266,7 +274,9 @@ def pytest_make_collect_report(collector):
|
||||
if not call.excinfo:
|
||||
outcome = "passed"
|
||||
else:
|
||||
if call.excinfo.errisinstance(collector.skip_exceptions):
|
||||
from _pytest import nose
|
||||
skip_exceptions = (Skipped,) + nose.get_skip_exceptions()
|
||||
if call.excinfo.errisinstance(skip_exceptions):
|
||||
outcome = "skipped"
|
||||
r = collector._repr_failure_py(call.excinfo, "line").reprcrash
|
||||
longrepr = (str(r.path), r.lineno, r.message)
|
||||
@@ -283,7 +293,8 @@ def pytest_make_collect_report(collector):
|
||||
|
||||
|
||||
class CollectReport(BaseReport):
|
||||
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
|
||||
def __init__(self, nodeid, outcome, longrepr, result,
|
||||
sections=(), **extra):
|
||||
self.nodeid = nodeid
|
||||
self.outcome = outcome
|
||||
self.longrepr = longrepr
|
||||
@@ -315,11 +326,10 @@ class SetupState(object):
|
||||
""" attach a finalizer to the given colitem.
|
||||
if colitem is None, this will add a finalizer that
|
||||
is called at the end of teardown_all().
|
||||
if colitem is a tuple, it will be used as a key
|
||||
and needs an explicit call to _callfinalizers(key) later on.
|
||||
"""
|
||||
assert hasattr(finalizer, '__call__')
|
||||
#assert colitem in self.stack
|
||||
assert colitem and not isinstance(colitem, tuple)
|
||||
assert py.builtin.callable(finalizer)
|
||||
#assert colitem in self.stack # some unit tests don't setup stack :/
|
||||
self._finalizers.setdefault(colitem, []).append(finalizer)
|
||||
|
||||
def _pop_and_teardown(self):
|
||||
@@ -328,9 +338,18 @@ class SetupState(object):
|
||||
|
||||
def _callfinalizers(self, colitem):
|
||||
finalizers = self._finalizers.pop(colitem, None)
|
||||
exc = None
|
||||
while finalizers:
|
||||
fin = finalizers.pop()
|
||||
fin()
|
||||
try:
|
||||
fin()
|
||||
except Exception:
|
||||
# XXX Only first exception will be seen by user,
|
||||
# ideally all should be reported.
|
||||
if exc is None:
|
||||
exc = sys.exc_info()
|
||||
if exc:
|
||||
py.builtin._reraise(*exc)
|
||||
|
||||
def _teardown_with_finalization(self, colitem):
|
||||
self._callfinalizers(colitem)
|
||||
@@ -410,7 +429,7 @@ class Skipped(OutcomeException):
|
||||
__module__ = 'builtins'
|
||||
|
||||
class Failed(OutcomeException):
|
||||
""" raised from an explicit call to py.test.fail() """
|
||||
""" raised from an explicit call to pytest.fail() """
|
||||
__module__ = 'builtins'
|
||||
|
||||
class Exit(KeyboardInterrupt):
|
||||
@@ -430,7 +449,7 @@ exit.Exception = Exit
|
||||
|
||||
def skip(msg=""):
|
||||
""" skip an executing test with the given message. Note: it's usually
|
||||
better to use the py.test.mark.skipif marker to declare a test to be
|
||||
better to use the pytest.mark.skipif marker to declare a test to be
|
||||
skipped under certain conditions like mismatching platforms or
|
||||
dependencies. See the pytest_skipping plugin for details.
|
||||
"""
|
||||
@@ -450,25 +469,25 @@ fail.Exception = Failed
|
||||
|
||||
|
||||
def importorskip(modname, minversion=None):
|
||||
""" return imported module if it has a higher __version__ than the
|
||||
optionally specified 'minversion' - otherwise call py.test.skip()
|
||||
with a message detailing the mismatch.
|
||||
""" return imported module if it has at least "minversion" as its
|
||||
__version__ attribute. If no minversion is specified the a skip
|
||||
is only triggered if the module can not be imported.
|
||||
Note that version comparison only works with simple version strings
|
||||
like "1.2.3" but not "1.2.3.dev1" or others.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
compile(modname, '', 'eval') # to catch syntaxerrors
|
||||
try:
|
||||
__import__(modname)
|
||||
except ImportError:
|
||||
py.test.skip("could not import %r" %(modname,))
|
||||
skip("could not import %r" %(modname,))
|
||||
mod = sys.modules[modname]
|
||||
if minversion is None:
|
||||
return mod
|
||||
verattr = getattr(mod, '__version__', None)
|
||||
if isinstance(minversion, str):
|
||||
minver = minversion.split(".")
|
||||
else:
|
||||
minver = list(minversion)
|
||||
if verattr is None or verattr.split(".") < minver:
|
||||
py.test.skip("module %r has __version__ %r, required is: %r" %(
|
||||
modname, verattr, minversion))
|
||||
def intver(verstring):
|
||||
return [int(x) for x in verstring.split(".")]
|
||||
if verattr is None or intver(verattr) < intver(minversion):
|
||||
skip("module %r has __version__ %r, required is: %r" %(
|
||||
modname, verattr, minversion))
|
||||
return mod
|
||||
|
||||
@@ -10,6 +10,14 @@ def pytest_addoption(parser):
|
||||
help="run tests even if they are marked xfail")
|
||||
|
||||
def pytest_configure(config):
|
||||
if config.option.runxfail:
|
||||
old = pytest.xfail
|
||||
config._cleanup.append(lambda: setattr(pytest, "xfail", old))
|
||||
def nop(*args, **kwargs):
|
||||
pass
|
||||
nop.Exception = XFailed
|
||||
setattr(pytest, "xfail", nop)
|
||||
|
||||
config.addinivalue_line("markers",
|
||||
"skipif(condition): skip the given test function if eval(condition) "
|
||||
"results in a True value. Evaluation happens within the "
|
||||
@@ -29,7 +37,7 @@ def pytest_namespace():
|
||||
return dict(xfail=xfail)
|
||||
|
||||
class XFailed(pytest.fail.Exception):
|
||||
""" raised from an explicit call to py.test.xfail() """
|
||||
""" raised from an explicit call to pytest.xfail() """
|
||||
|
||||
def xfail(reason=""):
|
||||
""" xfail an executing test or setup functions with the given reason."""
|
||||
@@ -121,7 +129,7 @@ def pytest_runtest_setup(item):
|
||||
return
|
||||
evalskip = MarkEvaluator(item, 'skipif')
|
||||
if evalskip.istrue():
|
||||
py.test.skip(evalskip.getexplanation())
|
||||
pytest.skip(evalskip.getexplanation())
|
||||
item._evalxfail = MarkEvaluator(item, 'xfail')
|
||||
check_xfail_no_run(item)
|
||||
|
||||
@@ -133,7 +141,7 @@ def check_xfail_no_run(item):
|
||||
evalxfail = item._evalxfail
|
||||
if evalxfail.istrue():
|
||||
if not evalxfail.get('run', True):
|
||||
py.test.xfail("[NOTRUN] " + evalxfail.getexplanation())
|
||||
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
|
||||
|
||||
def pytest_runtest_makereport(__multicall__, item, call):
|
||||
if not isinstance(item, pytest.Function):
|
||||
@@ -142,16 +150,16 @@ def pytest_runtest_makereport(__multicall__, item, call):
|
||||
if hasattr(item, '_unexpectedsuccess'):
|
||||
rep = __multicall__.execute()
|
||||
if rep.when == "call":
|
||||
# we need to translate into how py.test encodes xpass
|
||||
# we need to translate into how pytest encodes xpass
|
||||
rep.wasxfail = "reason: " + repr(item._unexpectedsuccess)
|
||||
rep.outcome = "failed"
|
||||
return rep
|
||||
if not (call.excinfo and
|
||||
call.excinfo.errisinstance(py.test.xfail.Exception)):
|
||||
call.excinfo.errisinstance(pytest.xfail.Exception)):
|
||||
evalxfail = getattr(item, '_evalxfail', None)
|
||||
if not evalxfail:
|
||||
return
|
||||
if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception):
|
||||
if call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception):
|
||||
if not item.config.getvalue("runxfail"):
|
||||
rep = __multicall__.execute()
|
||||
rep.wasxfail = "reason: " + call.excinfo.value.msg
|
||||
@@ -178,7 +186,7 @@ def pytest_report_teststatus(report):
|
||||
if report.skipped:
|
||||
return "xfailed", "x", "xfail"
|
||||
elif report.failed:
|
||||
return "xpassed", "X", "XPASS"
|
||||
return "xpassed", "X", ("XPASS", {'yellow': True})
|
||||
|
||||
# called by the terminalreporter instance/plugin
|
||||
def pytest_terminal_summary(terminalreporter):
|
||||
@@ -209,7 +217,6 @@ def pytest_terminal_summary(terminalreporter):
|
||||
tr._tw.line(line)
|
||||
|
||||
def show_simple(terminalreporter, lines, stat, format):
|
||||
tw = terminalreporter._tw
|
||||
failed = terminalreporter.stats.get(stat)
|
||||
if failed:
|
||||
for rep in failed:
|
||||
|
||||
@@ -39,7 +39,7 @@ class DictImporter(object):
|
||||
if is_pkg:
|
||||
module.__path__ = [fullname]
|
||||
|
||||
do_exec(co, module.__dict__)
|
||||
do_exec(co, module.__dict__) # noqa
|
||||
return sys.modules[fullname]
|
||||
|
||||
def get_source(self, name):
|
||||
@@ -63,4 +63,4 @@ if __name__ == "__main__":
|
||||
sys.meta_path.insert(0, importer)
|
||||
|
||||
entry = "@ENTRY@"
|
||||
do_exec(entry, locals())
|
||||
do_exec(entry, locals()) # noqa
|
||||
|
||||
@@ -5,7 +5,6 @@ This is a good source for looking at the various reporting hooks.
|
||||
import pytest
|
||||
import py
|
||||
import sys
|
||||
import os
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("terminal reporting", "reporting", after="general")
|
||||
@@ -16,7 +15,7 @@ def pytest_addoption(parser):
|
||||
group._addoption('-r',
|
||||
action="store", dest="reportchars", default=None, metavar="chars",
|
||||
help="show extra test summary info as specified by chars (f)ailed, "
|
||||
"(E)error, (s)skipped, (x)failed, (X)passed.")
|
||||
"(E)error, (s)skipped, (x)failed, (X)passed (w)warnings.")
|
||||
group._addoption('-l', '--showlocals',
|
||||
action="store_true", dest="showlocals", default=False,
|
||||
help="show locals in tracebacks (disabled by default).")
|
||||
@@ -24,12 +23,16 @@ def pytest_addoption(parser):
|
||||
action="store", dest="report", default=None, metavar="opts",
|
||||
help="(deprecated, use -r)")
|
||||
group._addoption('--tb', metavar="style",
|
||||
action="store", dest="tbstyle", default='long',
|
||||
choices=['long', 'short', 'no', 'line', 'native'],
|
||||
action="store", dest="tbstyle", default='auto',
|
||||
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
|
||||
help="traceback print mode (long/short/line/native/no).")
|
||||
group._addoption('--fulltrace', '--full-trace',
|
||||
action="store_true", default=False,
|
||||
help="don't cut any tracebacks (default is to cut).")
|
||||
group._addoption('--color', metavar="color",
|
||||
action="store", dest="color", default='auto',
|
||||
choices=['yes', 'no', 'auto'],
|
||||
help="color terminal output (yes/no/auto).")
|
||||
|
||||
def pytest_configure(config):
|
||||
config.option.verbose -= config.option.quiet
|
||||
@@ -72,6 +75,14 @@ def pytest_report_teststatus(report):
|
||||
letter = "f"
|
||||
return report.outcome, letter, report.outcome.upper()
|
||||
|
||||
class WarningReport:
|
||||
def __init__(self, code, message, nodeid=None, fslocation=None):
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.nodeid = nodeid
|
||||
self.fslocation = fslocation
|
||||
|
||||
|
||||
class TerminalReporter:
|
||||
def __init__(self, config, file=None):
|
||||
self.config = config
|
||||
@@ -86,6 +97,10 @@ class TerminalReporter:
|
||||
if file is None:
|
||||
file = py.std.sys.stdout
|
||||
self._tw = self.writer = py.io.TerminalWriter(file)
|
||||
if self.config.option.color == 'yes':
|
||||
self._tw.hasmarkup = True
|
||||
if self.config.option.color == 'no':
|
||||
self._tw.hasmarkup = False
|
||||
self.currentfspath = None
|
||||
self.reportchars = getreportopt(config)
|
||||
self.hasmarkup = self._tw.hasmarkup
|
||||
@@ -121,7 +136,8 @@ class TerminalReporter:
|
||||
self._tw.write(content, **markup)
|
||||
|
||||
def write_line(self, line, **markup):
|
||||
line = str(line)
|
||||
if not py.builtin._istext(line):
|
||||
line = py.builtin.text(line, errors="replace")
|
||||
self.ensure_newline()
|
||||
self._tw.line(line, **markup)
|
||||
|
||||
@@ -140,10 +156,16 @@ class TerminalReporter:
|
||||
self._tw.line(msg, **kw)
|
||||
|
||||
def pytest_internalerror(self, excrepr):
|
||||
for line in str(excrepr).split("\n"):
|
||||
for line in py.builtin.text(excrepr).split("\n"):
|
||||
self.write_line("INTERNALERROR> " + line)
|
||||
return 1
|
||||
|
||||
def pytest_logwarning(self, code, fslocation, message, nodeid):
|
||||
warnings = self.stats.setdefault("warnings", [])
|
||||
warning = WarningReport(code=code, fslocation=fslocation,
|
||||
message=message, nodeid=nodeid)
|
||||
warnings.append(warning)
|
||||
|
||||
def pytest_plugin_registered(self, plugin):
|
||||
if self.config.option.traceconfig:
|
||||
msg = "PLUGIN registered: %s" % (plugin,)
|
||||
@@ -252,7 +274,7 @@ class TerminalReporter:
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
|
||||
msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
|
||||
msg += " -- pytest-%s" % (py.test.__version__)
|
||||
msg += " -- py-%s -- pytest-%s" % (py.__version__, pytest.__version__)
|
||||
if self.verbosity > 0 or self.config.option.debug or \
|
||||
getattr(self.config.option, 'pastebin', None):
|
||||
msg += " -- " + str(sys.executable)
|
||||
@@ -327,7 +349,7 @@ class TerminalReporter:
|
||||
if exitstatus in (0, 1, 2, 4):
|
||||
self.summary_errors()
|
||||
self.summary_failures()
|
||||
self.summary_hints()
|
||||
self.summary_warnings()
|
||||
self.config.hook.pytest_terminal_summary(terminalreporter=self)
|
||||
if exitstatus == 2:
|
||||
self._report_keyboardinterrupt()
|
||||
@@ -360,9 +382,11 @@ class TerminalReporter:
|
||||
line = str(fspath)
|
||||
if lineno is not None:
|
||||
lineno += 1
|
||||
line += ":" + str(lineno)
|
||||
line += "@" + str(lineno)
|
||||
if domain:
|
||||
line += ": " + str(domain)
|
||||
split = str(domain).split('[')
|
||||
split[0] = split[0].replace('.', '::') # don't replace '.' in params
|
||||
line += "::" + '['.join(split)
|
||||
else:
|
||||
line = "[location]"
|
||||
return line + " "
|
||||
@@ -393,10 +417,15 @@ class TerminalReporter:
|
||||
l.append(x)
|
||||
return l
|
||||
|
||||
def summary_hints(self):
|
||||
if self.config.option.traceconfig:
|
||||
for hint in self.config.pluginmanager._hints:
|
||||
self._tw.line("hint: %s" % hint)
|
||||
def summary_warnings(self):
|
||||
if self.hasopt("w"):
|
||||
warnings = self.stats.get("warnings")
|
||||
if not warnings:
|
||||
return
|
||||
self.write_sep("=", "warning summary")
|
||||
for w in warnings:
|
||||
self._tw.line("W%s %s %s" % (w.code,
|
||||
w.fslocation, w.message))
|
||||
|
||||
def summary_failures(self):
|
||||
if self.config.option.tbstyle != "no":
|
||||
@@ -442,7 +471,8 @@ class TerminalReporter:
|
||||
def summary_stats(self):
|
||||
session_duration = py.std.time.time() - self._sessionstarttime
|
||||
|
||||
keys = "failed passed skipped deselected xfailed xpassed".split()
|
||||
keys = ("failed passed skipped deselected "
|
||||
"xfailed xpassed warnings").split()
|
||||
for key in self.stats.keys():
|
||||
if key not in keys:
|
||||
keys.append(key)
|
||||
|
||||
@@ -41,17 +41,17 @@ class UnitTestCase(pytest.Class):
|
||||
super(UnitTestCase, self).setup()
|
||||
|
||||
def collect(self):
|
||||
cls = self.obj
|
||||
if not getattr(cls, "__test__", True):
|
||||
return
|
||||
self.session._fixturemanager.parsefactories(self, unittest=True)
|
||||
loader = py.std.unittest.TestLoader()
|
||||
module = self.getparent(pytest.Module).obj
|
||||
cls = self.obj
|
||||
foundsomething = False
|
||||
for name in loader.getTestCaseNames(self.obj):
|
||||
x = getattr(self.obj, name)
|
||||
funcobj = getattr(x, 'im_func', x)
|
||||
transfer_markers(funcobj, cls, module)
|
||||
if hasattr(funcobj, 'todo'):
|
||||
pytest.mark.xfail(reason=str(funcobj.todo))(funcobj)
|
||||
yield TestCaseFunction(name, parent=self)
|
||||
foundsomething = True
|
||||
|
||||
@@ -70,10 +70,6 @@ class TestCaseFunction(pytest.Function):
|
||||
def setup(self):
|
||||
self._testcase = self.parent.obj(self.name)
|
||||
self._obj = getattr(self._testcase, self.name)
|
||||
if hasattr(self._testcase, 'skip'):
|
||||
pytest.skip(self._testcase.skip)
|
||||
if hasattr(self._obj, 'skip'):
|
||||
pytest.skip(self._obj.skip)
|
||||
if hasattr(self._testcase, 'setup_method'):
|
||||
self._testcase.setup_method(self._obj)
|
||||
if hasattr(self, "_request"):
|
||||
@@ -160,7 +156,7 @@ def pytest_runtest_protocol(item, __multicall__):
|
||||
if isinstance(item, TestCaseFunction):
|
||||
if 'twisted.trial.unittest' in sys.modules:
|
||||
ut = sys.modules['twisted.python.failure']
|
||||
Failure__init__ = ut.Failure.__init__.im_func
|
||||
Failure__init__ = ut.Failure.__init__
|
||||
check_testcase_implements_trial_reporter()
|
||||
def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
|
||||
captureVars=None):
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
import cProfile
|
||||
import py
|
||||
import pytest
|
||||
import pstats
|
||||
stats = cProfile.run('py.test.cmdline.main(["empty.py", ])', 'prof')
|
||||
script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py"
|
||||
stats = cProfile.run('pytest.cmdline.main(%r)' % script, 'prof')
|
||||
p = pstats.Stats("prof")
|
||||
p.strip_dirs()
|
||||
p.sort_stats('cumulative')
|
||||
print(p.print_stats(50))
|
||||
print(p.print_stats(250))
|
||||
|
||||
12
bench/manyparam.py
Normal file
12
bench/manyparam.py
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='module', params=range(966))
|
||||
def foo(request):
|
||||
return request.param
|
||||
|
||||
def test_it(foo):
|
||||
pass
|
||||
def test_it2(foo):
|
||||
pass
|
||||
|
||||
10
bench/skip.py
Normal file
10
bench/skip.py
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
SKIP = True
|
||||
|
||||
@pytest.mark.parametrize("x", xrange(5000))
|
||||
def test_foo(x):
|
||||
if SKIP:
|
||||
pytest.skip("heh")
|
||||
@@ -1,339 +0,0 @@
|
||||
/*
|
||||
* sphinxdoc.css_t
|
||||
* ~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
|
||||
* Armin Ronacher for Werkzeug.
|
||||
*
|
||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
|
||||
'Verdana', sans-serif;
|
||||
font-size: 1.1em;
|
||||
letter-spacing: -0.01em;
|
||||
line-height: 150%;
|
||||
text-align: center;
|
||||
background-color: #BFD1D4;
|
||||
color: black;
|
||||
padding: 0;
|
||||
border: 1px solid #aaa;
|
||||
|
||||
margin: 0px 80px 0px 80px;
|
||||
min-width: 740px;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
background-image: url(contents.png);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 240px 0 0;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.body {
|
||||
margin: 0;
|
||||
padding: 0.5em 20px 20px 20px;
|
||||
}
|
||||
|
||||
div.related {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
background-image: url(navigation.png);
|
||||
height: 2em;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
div.related ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 2em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.related ul li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
div.related ul li a {
|
||||
margin: 0;
|
||||
padding: 0 5px 0 5px;
|
||||
line-height: 1.75em;
|
||||
color: #EE9816;
|
||||
}
|
||||
|
||||
div.related ul li a:hover {
|
||||
color: #3CA8E7;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
margin: 0;
|
||||
padding: 0.5em 15px 15px 0;
|
||||
width: 210px;
|
||||
float: right;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4 {
|
||||
margin: 1em 0 0.5em 0;
|
||||
font-size: 1em;
|
||||
padding: 0.1em 0 0.1em 0.5em;
|
||||
color: white;
|
||||
border: 1px solid #86989B;
|
||||
background-color: #AFC1C4;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
padding-left: 1.5em;
|
||||
margin-top: 7px;
|
||||
padding: 0;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
background-color: #E3EFF1;
|
||||
color: #86989B;
|
||||
padding: 3px 8px 3px 0;
|
||||
clear: both;
|
||||
font-size: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #86989B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
p {
|
||||
margin: 0.8em 0 0.5em 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #CA7900;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #2491CF;
|
||||
}
|
||||
|
||||
div.body a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 0.7em 0 0.3em 0;
|
||||
font-size: 1.5em;
|
||||
color: #11557C;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 1.3em 0 0.2em 0;
|
||||
font-size: 1.35em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1em 0 -0.3em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
|
||||
color: black!important;
|
||||
}
|
||||
|
||||
h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
|
||||
display: none;
|
||||
margin: 0 0 0 0.3em;
|
||||
padding: 0 0.2em 0 0.2em;
|
||||
color: #aaa!important;
|
||||
}
|
||||
|
||||
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
|
||||
h5:hover a.anchor, h6:hover a.anchor {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
|
||||
h5 a.anchor:hover, h6 a.anchor:hover {
|
||||
color: #777;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
a.headerlink {
|
||||
color: #c60f0f!important;
|
||||
font-size: 1em;
|
||||
margin-left: 6px;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none!important;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: #ccc;
|
||||
color: white!important;
|
||||
}
|
||||
|
||||
cite, code, tt {
|
||||
font-family: 'Consolas', 'Deja Vu Sans Mono',
|
||||
'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.95em;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #f2f2f2;
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname, tt.xref {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #abc;
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
a tt {
|
||||
border: 0;
|
||||
color: #CA7900;
|
||||
}
|
||||
|
||||
a tt:hover {
|
||||
color: #2491CF;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Consolas', 'Deja Vu Sans Mono',
|
||||
'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.95em;
|
||||
letter-spacing: 0.015em;
|
||||
line-height: 120%;
|
||||
padding: 0.5em;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
pre a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
div.quotebar {
|
||||
background-color: #f8f8f8;
|
||||
max-width: 250px;
|
||||
float: right;
|
||||
padding: 2px 7px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 0 -0.5em 0 -0.5em;
|
||||
}
|
||||
|
||||
table td, table th {
|
||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||
}
|
||||
|
||||
div.admonition, div.warning {
|
||||
font-size: 0.9em;
|
||||
margin: 1em 0 1em 0;
|
||||
border: 1px solid #86989B;
|
||||
background-color: #f7f7f7;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.admonition p, div.warning p {
|
||||
margin: 0.5em 1em 0.5em 1em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.admonition pre, div.warning pre {
|
||||
margin: 0.4em 1em 0.4em 1em;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title,
|
||||
div.warning p.admonition-title {
|
||||
margin: 0;
|
||||
padding: 0.1em 0 0.1em 0.5em;
|
||||
color: white;
|
||||
border-bottom: 1px solid #86989B;
|
||||
font-weight: bold;
|
||||
background-color: #AFC1C4;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
border: 1px solid #940000;
|
||||
}
|
||||
|
||||
div.warning p.admonition-title {
|
||||
background-color: #CF0000;
|
||||
border-bottom-color: #940000;
|
||||
}
|
||||
|
||||
div.admonition ul, div.admonition ol,
|
||||
div.warning ul, div.warning ol {
|
||||
margin: 0.1em 0.5em 0.5em 3em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.versioninfo {
|
||||
margin: 1em 0 0 0;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #DDEAF0;
|
||||
padding: 8px;
|
||||
line-height: 1.3em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
|
||||
'Verdana', sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
||||
17
doc/en/_templates/globaltoc.html
Normal file
17
doc/en/_templates/globaltoc.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ pathto('index') }}">Home</a></li>
|
||||
<li><a href="{{ pathto('contents') }}">Contents</a></li>
|
||||
<li><a href="{{ pathto('getting-started') }}">Install</a></li>
|
||||
<li><a href="{{ pathto('example/index') }}">Examples</a></li>
|
||||
<li><a href="{{ pathto('customize') }}">Customize</a></li>
|
||||
<li><a href="{{ pathto('contact') }}">Contact</a></li>
|
||||
<li><a href="{{ pathto('talks') }}">Talks/Posts</a></li>
|
||||
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
|
||||
</ul>
|
||||
|
||||
{%- if display_toc %}
|
||||
<hr>
|
||||
{{ toc }}
|
||||
{%- endif %}
|
||||
@@ -1,22 +0,0 @@
|
||||
<h3>Download</h3>
|
||||
{% if version.endswith('(hg)') %}
|
||||
<p>This documentation is for version <b>{{ version }}</b>, which is
|
||||
not released yet.</p>
|
||||
<p>You can use it from the
|
||||
<a href="http://bitbucket.org/hpk42/pytest">Bitbucket Repo</a> or look for
|
||||
released versions in the <a href="http://pypi.python.org/pypi/pytest">Python
|
||||
Package Index</a>.</p>
|
||||
{% else %}
|
||||
<p><b><a href="{{ pathto('announce/index')}}">{{ release }} release</a></b>
|
||||
[<a href="{{ pathto('changelog') }}">Changelog</a>]</p>
|
||||
<p>
|
||||
<a href="http://pypi.python.org/pypi/pytest">pytest/PyPI</a>
|
||||
</p>
|
||||
<pre>easy_install pytest</pre>
|
||||
<pre>pip install pytest</pre>
|
||||
{% endif %}
|
||||
|
||||
<h3>Questions? Suggestions?</h3>
|
||||
|
||||
<p><a href="{{ pathto('contact') }}">contact channels</a>
|
||||
</p>
|
||||
@@ -1,13 +1,5 @@
|
||||
{% extends "!layout.html" %}
|
||||
|
||||
{% block relbaritems %}
|
||||
{{ super() }}
|
||||
<g:plusone></g:plusone>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
@@ -23,5 +15,4 @@
|
||||
})();
|
||||
|
||||
</script>
|
||||
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
11
doc/en/_templates/links.html
Normal file
11
doc/en/_templates/links.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto('index') }}">The pytest Website</a></li>
|
||||
<li><a href="{{ pathto('contributing') }}">Contribution Guide</a></li>
|
||||
<li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li>
|
||||
<li><a href="https://bitbucket.org/hpk42/pytest/">pytest @ Bitbucket</a></li>
|
||||
<li><a href="http://pytest.org/latest/plugins_index/index.html">3rd party plugins</a></li>
|
||||
<li><a href="https://bitbucket.org/hpk42/pytest/issues?status=new&status=open">Issue Tracker</a></li>
|
||||
<li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a>
|
||||
</ul>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
{%- if pagename != "search" %}
|
||||
<div id="searchbox" style="display: none">
|
||||
<form class="search" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" size="18" />
|
||||
<input type="submit" value="{{ _('Search') }}" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
{%- endif %}
|
||||
|
||||
<h3>quicklinks</h3>
|
||||
<div style="text-align: left; font-size: 100%; vertical-align: middle;">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ pathto('index') }}">home</a>
|
||||
</td><td>
|
||||
<a href="{{ pathto('contents') }}">TOC/contents</a>
|
||||
</td></tr><tr><td>
|
||||
<a href="{{ pathto('getting-started') }}">install</a>
|
||||
</td><td>
|
||||
<a href="{{ pathto('changelog') }}">changelog</a>
|
||||
</td></tr><tr><td>
|
||||
<a href="{{ pathto('example/index') }}">examples</a>
|
||||
</td><td>
|
||||
<a href="{{ pathto('customize') }}">customize</a>
|
||||
</td></tr><tr><td>
|
||||
<a href="https://bitbucket.org/hpk42/pytest/issues?status=new&status=open">issues[bb]</a>
|
||||
</td><td>
|
||||
<a href="{{ pathto('contact') }}">contact</a>
|
||||
</td></tr><tr><td>
|
||||
<a href="{{ pathto('talks') }}">Talks/Posts</a>
|
||||
</td></tr></table>
|
||||
</div>
|
||||
{% extends "basic/localtoc.html" %}
|
||||
|
||||
5
doc/en/_templates/sidebarintro.html
Normal file
5
doc/en/_templates/sidebarintro.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<h3>About pytest</h3>
|
||||
<p>
|
||||
pytest is a mature full-featured Python testing tool that helps
|
||||
you write better programs.
|
||||
</p>
|
||||
3
doc/en/_themes/.gitignore
vendored
Normal file
3
doc/en/_themes/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
.DS_Store
|
||||
37
doc/en/_themes/LICENSE
Normal file
37
doc/en/_themes/LICENSE
Normal file
@@ -0,0 +1,37 @@
|
||||
Copyright (c) 2010 by Armin Ronacher.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms of the theme, with or
|
||||
without modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
We kindly ask you to only use these themes in an unmodified manner just
|
||||
for Flask and Flask-related products, not for unrelated projects. If you
|
||||
like the visual style and want to use it for your own projects, please
|
||||
consider making some larger changes to the themes (such as changing
|
||||
font faces, sizes, colors or margins).
|
||||
|
||||
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
31
doc/en/_themes/README
Normal file
31
doc/en/_themes/README
Normal file
@@ -0,0 +1,31 @@
|
||||
Flask Sphinx Styles
|
||||
===================
|
||||
|
||||
This repository contains sphinx styles for Flask and Flask related
|
||||
projects. To use this style in your Sphinx documentation, follow
|
||||
this guide:
|
||||
|
||||
1. put this folder as _themes into your docs folder. Alternatively
|
||||
you can also use git submodules to check out the contents there.
|
||||
2. add this to your conf.py:
|
||||
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
html_theme = 'flask'
|
||||
|
||||
The following themes exist:
|
||||
|
||||
- 'flask' - the standard flask documentation theme for large
|
||||
projects
|
||||
- 'flask_small' - small one-page theme. Intended to be used by
|
||||
very small addon libraries for flask.
|
||||
|
||||
The following options exist for the flask_small theme:
|
||||
|
||||
[options]
|
||||
index_logo = '' filename of a picture in _static
|
||||
to be used as replacement for the
|
||||
h1 in the index.rst file.
|
||||
index_logo_height = 120px height of the index logo
|
||||
github_fork = '' repository name on github for the
|
||||
"fork me" badge
|
||||
24
doc/en/_themes/flask/layout.html
Normal file
24
doc/en/_themes/flask/layout.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{%- extends "basic/layout.html" %}
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
{% if theme_touch_icon %}
|
||||
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
|
||||
{% endblock %}
|
||||
{%- block relbar2 %}{% endblock %}
|
||||
{% block header %}
|
||||
{{ super() }}
|
||||
{% if pagename == 'index' %}
|
||||
<div class=indexwrapper>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
© Copyright {{ copyright }}.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
{% if pagename == 'index' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- endblock %}
|
||||
19
doc/en/_themes/flask/relations.html
Normal file
19
doc/en/_themes/flask/relations.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
|
||||
{%- for parent in parents %}
|
||||
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
|
||||
{%- endfor %}
|
||||
{%- if prev %}
|
||||
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
|
||||
}}">{{ prev.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
|
||||
}}">{{ next.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- for parent in parents %}
|
||||
</ul></li>
|
||||
{%- endfor %}
|
||||
</ul></li>
|
||||
</ul>
|
||||
556
doc/en/_themes/flask/static/flasky.css_t
Normal file
556
doc/en/_themes/flask/static/flasky.css_t
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* :copyright: Copyright 2010 by Armin Ronacher.
|
||||
* :license: Flask Design License, see LICENSE for details.
|
||||
*/
|
||||
|
||||
{% set page_width = '1020px' %}
|
||||
{% set sidebar_width = '220px' %}
|
||||
/* orange of logo is #d67c29 but we use black for links for now */
|
||||
{% set link_color = '#000' %}
|
||||
{% set link_hover_color = '#000' %}
|
||||
{% set base_font = 'sans-serif' %}
|
||||
{% set header_font = 'sans-serif' %}
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: {{ base_font }};
|
||||
font-size: 17px;
|
||||
background-color: white;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: {{ page_width }};
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 {{ sidebar_width }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: {{ sidebar_width }};
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 0 30px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
width: {{ page_width }};
|
||||
margin: 20px auto 30px auto;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 18px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.logo {
|
||||
padding: 0 0 20px 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: {{ header_font }};
|
||||
color: #444;
|
||||
font-size: 24px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo a,
|
||||
div.sphinxsidebar h3 a,
|
||||
div.sphinxsidebar p.logo a:hover,
|
||||
div.sphinxsidebar h3 a:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #555;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #ccc;
|
||||
font-family: {{ base_font }};
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: {{ link_color }};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: {{ link_hover_color }};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a.reference.internal em {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: {{ header_font }};
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
{% if theme_index_logo %}
|
||||
div.indexwrapper h1 {
|
||||
text-indent: -999999px;
|
||||
background: url({{ theme_index_logo }}) no-repeat center center;
|
||||
height: {{ theme_index_logo_height }};
|
||||
}
|
||||
{% else %}
|
||||
div.indexwrapper div.body h1 {
|
||||
font-size: 200%;
|
||||
}
|
||||
{% endif %}
|
||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #ddd;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition tt.xref, div.admonition a tt {
|
||||
border-bottom: 1px solid #fafafa;
|
||||
}
|
||||
|
||||
dd div.admonition {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: {{ header_font }};
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
background: #fdfdfd;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.footnote + table.footnote {
|
||||
margin-top: -15px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td.label {
|
||||
width: 0px;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #eee;
|
||||
padding: 7px 30px;
|
||||
margin: 15px -30px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
dl dl pre {
|
||||
margin-left: -90px;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
a.reference {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted {{ link_color }};
|
||||
}
|
||||
|
||||
a.reference:hover {
|
||||
border-bottom: 1px solid {{ link_hover_color }};
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px dotted {{ link_color }};
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px solid {{ link_hover_color }};
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 870px) {
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 875px) {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: none;
|
||||
background: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: block;
|
||||
float: none;
|
||||
width: 102.5%;
|
||||
margin: 50px -30px -20px -30px;
|
||||
padding: 10px 20px;
|
||||
background: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
||||
div.sphinxsidebar h3 a, div.sphinxsidebar ul {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
div.related ul,
|
||||
div.related ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rtd_doc_footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* misc. */
|
||||
|
||||
.revsys-inline {
|
||||
display: none!important;
|
||||
}
|
||||
9
doc/en/_themes/flask/theme.conf
Normal file
9
doc/en/_themes/flask/theme.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
index_logo = ''
|
||||
index_logo_height = 120px
|
||||
touch_icon =
|
||||
86
doc/en/_themes/flask_theme_support.py
Normal file
86
doc/en/_themes/flask_theme_support.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# flasky extensions. flasky pygments style based on tango style
|
||||
from pygments.style import Style
|
||||
from pygments.token import Keyword, Name, Comment, String, Error, \
|
||||
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
|
||||
|
||||
|
||||
class FlaskyStyle(Style):
|
||||
background_color = "#f8f8f8"
|
||||
default_style = ""
|
||||
|
||||
styles = {
|
||||
# No corresponding class for the following:
|
||||
#Text: "", # class: ''
|
||||
Whitespace: "underline #f8f8f8", # class: 'w'
|
||||
Error: "#a40000 border:#ef2929", # class: 'err'
|
||||
Other: "#000000", # class 'x'
|
||||
|
||||
Comment: "italic #8f5902", # class: 'c'
|
||||
Comment.Preproc: "noitalic", # class: 'cp'
|
||||
|
||||
Keyword: "bold #004461", # class: 'k'
|
||||
Keyword.Constant: "bold #004461", # class: 'kc'
|
||||
Keyword.Declaration: "bold #004461", # class: 'kd'
|
||||
Keyword.Namespace: "bold #004461", # class: 'kn'
|
||||
Keyword.Pseudo: "bold #004461", # class: 'kp'
|
||||
Keyword.Reserved: "bold #004461", # class: 'kr'
|
||||
Keyword.Type: "bold #004461", # class: 'kt'
|
||||
|
||||
Operator: "#582800", # class: 'o'
|
||||
Operator.Word: "bold #004461", # class: 'ow' - like keywords
|
||||
|
||||
Punctuation: "bold #000000", # class: 'p'
|
||||
|
||||
# because special names such as Name.Class, Name.Function, etc.
|
||||
# are not recognized as such later in the parsing, we choose them
|
||||
# to look the same as ordinary variables.
|
||||
Name: "#000000", # class: 'n'
|
||||
Name.Attribute: "#c4a000", # class: 'na' - to be revised
|
||||
Name.Builtin: "#004461", # class: 'nb'
|
||||
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
|
||||
Name.Class: "#000000", # class: 'nc' - to be revised
|
||||
Name.Constant: "#000000", # class: 'no' - to be revised
|
||||
Name.Decorator: "#888", # class: 'nd' - to be revised
|
||||
Name.Entity: "#ce5c00", # class: 'ni'
|
||||
Name.Exception: "bold #cc0000", # class: 'ne'
|
||||
Name.Function: "#000000", # class: 'nf'
|
||||
Name.Property: "#000000", # class: 'py'
|
||||
Name.Label: "#f57900", # class: 'nl'
|
||||
Name.Namespace: "#000000", # class: 'nn' - to be revised
|
||||
Name.Other: "#000000", # class: 'nx'
|
||||
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
|
||||
Name.Variable: "#000000", # class: 'nv' - to be revised
|
||||
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
|
||||
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
|
||||
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
|
||||
|
||||
Number: "#990000", # class: 'm'
|
||||
|
||||
Literal: "#000000", # class: 'l'
|
||||
Literal.Date: "#000000", # class: 'ld'
|
||||
|
||||
String: "#4e9a06", # class: 's'
|
||||
String.Backtick: "#4e9a06", # class: 'sb'
|
||||
String.Char: "#4e9a06", # class: 'sc'
|
||||
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
|
||||
String.Double: "#4e9a06", # class: 's2'
|
||||
String.Escape: "#4e9a06", # class: 'se'
|
||||
String.Heredoc: "#4e9a06", # class: 'sh'
|
||||
String.Interpol: "#4e9a06", # class: 'si'
|
||||
String.Other: "#4e9a06", # class: 'sx'
|
||||
String.Regex: "#4e9a06", # class: 'sr'
|
||||
String.Single: "#4e9a06", # class: 's1'
|
||||
String.Symbol: "#4e9a06", # class: 'ss'
|
||||
|
||||
Generic: "#000000", # class: 'g'
|
||||
Generic.Deleted: "#a40000", # class: 'gd'
|
||||
Generic.Emph: "italic #000000", # class: 'ge'
|
||||
Generic.Error: "#ef2929", # class: 'gr'
|
||||
Generic.Heading: "bold #000080", # class: 'gh'
|
||||
Generic.Inserted: "#00A000", # class: 'gi'
|
||||
Generic.Output: "#888", # class: 'go'
|
||||
Generic.Prompt: "#745334", # class: 'gp'
|
||||
Generic.Strong: "bold #000000", # class: 'gs'
|
||||
Generic.Subheading: "bold #800080", # class: 'gu'
|
||||
Generic.Traceback: "bold #a40000", # class: 'gt'
|
||||
}
|
||||
@@ -5,6 +5,9 @@ Release announcements
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
release-2.5.2
|
||||
release-2.5.1
|
||||
release-2.5.0
|
||||
release-2.4.2
|
||||
release-2.4.1
|
||||
release-2.4.0
|
||||
|
||||
175
doc/en/announce/release-2.5.0.txt
Normal file
175
doc/en/announce/release-2.5.0.txt
Normal file
@@ -0,0 +1,175 @@
|
||||
pytest-2.5.0: now down to ZERO reported bugs!
|
||||
===========================================================================
|
||||
|
||||
pytest-2.5.0 is a big fixing release, the result of two community bug
|
||||
fixing days plus numerous additional works from many people and
|
||||
reporters. The release should be fully compatible to 2.4.2, existing
|
||||
plugins and test suites. We aim at maintaining this level of ZERO reported
|
||||
bugs because it's no fun if your testing tool has bugs, is it? Under a
|
||||
condition, though: when submitting a bug report please provide
|
||||
clear information about the circumstances and a simple example which
|
||||
reproduces the problem.
|
||||
|
||||
The issue tracker is of course not empty now. We have many remaining
|
||||
"enhacement" issues which we'll hopefully can tackle in 2014 with your
|
||||
help.
|
||||
|
||||
For those who use older Python versions, please note that pytest is not
|
||||
automatically tested on python2.5 due to virtualenv, setuptools and tox
|
||||
not supporting it anymore. Manual verification shows that it mostly
|
||||
works fine but it's not going to be part of the automated release
|
||||
process and thus likely to break in the future.
|
||||
|
||||
As usual, current docs are at
|
||||
|
||||
http://pytest.org
|
||||
|
||||
and you can upgrade from pypi via::
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Particular thanks for helping with this release go to Anatoly Bubenkoff,
|
||||
Floris Bruynooghe, Marc Abramowitz, Ralph Schmitt, Ronny Pfannschmidt,
|
||||
Donald Stufft, James Lan, Rob Dennis, Jason R. Coombs, Mathieu Agopian,
|
||||
Virgil Dupras, Bruno Oliveira, Alex Gaynor and others.
|
||||
|
||||
have fun,
|
||||
holger krekel
|
||||
|
||||
|
||||
2.5.0
|
||||
-----------------------------------
|
||||
|
||||
- dropped python2.5 from automated release testing of pytest itself
|
||||
which means it's probably going to break soon (but still works
|
||||
with this release we believe).
|
||||
|
||||
- simplified and fixed implementation for calling finalizers when
|
||||
parametrized fixtures or function arguments are involved. finalization
|
||||
is now performed lazily at setup time instead of in the "teardown phase".
|
||||
While this might sound odd at first, it helps to ensure that we are
|
||||
correctly handling setup/teardown even in complex code. User-level code
|
||||
should not be affected unless it's implementing the pytest_runtest_teardown
|
||||
hook and expecting certain fixture instances are torn down within (very
|
||||
unlikely and would have been unreliable anyway).
|
||||
|
||||
- PR90: add --color=yes|no|auto option to force terminal coloring
|
||||
mode ("auto" is default). Thanks Marc Abramowitz.
|
||||
|
||||
- fix issue319 - correctly show unicode in assertion errors. Many
|
||||
thanks to Floris Bruynooghe for the complete PR. Also means
|
||||
we depend on py>=1.4.19 now.
|
||||
|
||||
- fix issue396 - correctly sort and finalize class-scoped parametrized
|
||||
tests independently from number of methods on the class.
|
||||
|
||||
- refix issue323 in a better way -- parametrization should now never
|
||||
cause Runtime Recursion errors because the underlying algorithm
|
||||
for re-ordering tests per-scope/per-fixture is not recursive
|
||||
anymore (it was tail-call recursive before which could lead
|
||||
to problems for more than >966 non-function scoped parameters).
|
||||
|
||||
- fix issue290 - there is preliminary support now for parametrizing
|
||||
with repeated same values (sometimes useful to to test if calling
|
||||
a second time works as with the first time).
|
||||
|
||||
- close issue240 - document precisely how pytest module importing
|
||||
works, discuss the two common test directory layouts, and how it
|
||||
interacts with PEP420-namespace packages.
|
||||
|
||||
- fix issue246 fix finalizer order to be LIFO on independent fixtures
|
||||
depending on a parametrized higher-than-function scoped fixture.
|
||||
(was quite some effort so please bear with the complexity of this sentence :)
|
||||
Thanks Ralph Schmitt for the precise failure example.
|
||||
|
||||
- fix issue244 by implementing special index for parameters to only use
|
||||
indices for paramentrized test ids
|
||||
|
||||
- fix issue287 by running all finalizers but saving the exception
|
||||
from the first failing finalizer and re-raising it so teardown will
|
||||
still have failed. We reraise the first failing exception because
|
||||
it might be the cause for other finalizers to fail.
|
||||
|
||||
- fix ordering when mock.patch or other standard decorator-wrappings
|
||||
are used with test methods. This fixues issue346 and should
|
||||
help with random "xdist" collection failures. Thanks to
|
||||
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
|
||||
|
||||
- fix issue357 - special case "-k" expressions to allow for
|
||||
filtering with simple strings that are not valid python expressions.
|
||||
Examples: "-k 1.3" matches all tests parametrized with 1.3.
|
||||
"-k None" filters all tests that have "None" in their name
|
||||
and conversely "-k 'not None'".
|
||||
Previously these examples would raise syntax errors.
|
||||
|
||||
- fix issue384 by removing the trial support code
|
||||
since the unittest compat enhancements allow
|
||||
trial to handle it on its own
|
||||
|
||||
- don't hide an ImportError when importing a plugin produces one.
|
||||
fixes issue375.
|
||||
|
||||
- fix issue275 - allow usefixtures and autouse fixtures
|
||||
for running doctest text files.
|
||||
|
||||
- fix issue380 by making --resultlog only rely on longrepr instead
|
||||
of the "reprcrash" attribute which only exists sometimes.
|
||||
|
||||
- address issue122: allow @pytest.fixture(params=iterator) by exploding
|
||||
into a list early on.
|
||||
|
||||
- fix pexpect-3.0 compatibility for pytest's own tests.
|
||||
(fixes issue386)
|
||||
|
||||
- allow nested parametrize-value markers, thanks James Lan for the PR.
|
||||
|
||||
- fix unicode handling with new monkeypatch.setattr(import_path, value)
|
||||
API. Thanks Rob Dennis. Fixes issue371.
|
||||
|
||||
- fix unicode handling with junitxml, fixes issue368.
|
||||
|
||||
- In assertion rewriting mode on Python 2, fix the detection of coding
|
||||
cookies. See issue #330.
|
||||
|
||||
- make "--runxfail" turn imperative pytest.xfail calls into no ops
|
||||
(it already did neutralize pytest.mark.xfail markers)
|
||||
|
||||
- refine pytest / pkg_resources interactions: The AssertionRewritingHook
|
||||
PEP302 compliant loader now registers itself with setuptools/pkg_resources
|
||||
properly so that the pkg_resources.resource_stream method works properly.
|
||||
Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
|
||||
|
||||
- pytestconfig fixture is now session-scoped as it is the same object during the
|
||||
whole test run. Fixes issue370.
|
||||
|
||||
- avoid one surprising case of marker malfunction/confusion::
|
||||
|
||||
@pytest.mark.some(lambda arg: ...)
|
||||
def test_function():
|
||||
|
||||
would not work correctly because pytest assumes @pytest.mark.some
|
||||
gets a function to be decorated already. We now at least detect if this
|
||||
arg is an lambda and thus the example will work. Thanks Alex Gaynor
|
||||
for bringing it up.
|
||||
|
||||
- xfail a test on pypy that checks wrong encoding/ascii (pypy does
|
||||
not error out). fixes issue385.
|
||||
|
||||
- internally make varnames() deal with classes's __init__,
|
||||
although it's not needed by pytest itself atm. Also
|
||||
fix caching. Fixes issue376.
|
||||
|
||||
- fix issue221 - handle importing of namespace-package with no
|
||||
__init__.py properly.
|
||||
|
||||
- refactor internal FixtureRequest handling to avoid monkeypatching.
|
||||
One of the positive user-facing effects is that the "request" object
|
||||
can now be used in closures.
|
||||
|
||||
- fixed version comparison in pytest.importskip(modname, minverstring)
|
||||
|
||||
- fix issue377 by clarifying in the nose-compat docs that pytest
|
||||
does not duplicate the unittest-API into the "plain" namespace.
|
||||
|
||||
- fix verbose reporting for @mock'd test functions
|
||||
|
||||
47
doc/en/announce/release-2.5.1.txt
Normal file
47
doc/en/announce/release-2.5.1.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
pytest-2.5.1: fixes and new home page styling
|
||||
===========================================================================
|
||||
|
||||
pytest is a mature Python testing tool with more than a 1000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
The 2.5.1 release maintains the "zero-reported-bugs" promise by fixing
|
||||
the three bugs reported since the last release a few days ago. It also
|
||||
features a new home page styling implemented by Tobias Bieniek, based on
|
||||
the flask theme from Armin Ronacher:
|
||||
|
||||
http://pytest.org
|
||||
|
||||
If you have anything more to improve styling and docs,
|
||||
we'd be very happy to merge further pull requests.
|
||||
|
||||
On the coding side, the release also contains a little enhancement to
|
||||
fixture decorators allowing to directly influence generation of test
|
||||
ids, thanks to Floris Bruynooghe. Other thanks for helping with
|
||||
this release go to Anatoly Bubenkoff and Ronny Pfannschmidt.
|
||||
|
||||
As usual, you can upgrade from pypi via::
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
have fun and a nice remaining "bug-free" time of the year :)
|
||||
holger krekel
|
||||
|
||||
2.5.1
|
||||
-----------------------------------
|
||||
|
||||
- merge new documentation styling PR from Tobias Bieniek.
|
||||
|
||||
- fix issue403: allow parametrize of multiple same-name functions within
|
||||
a collection node. Thanks Andreas Kloeckner and Alex Gaynor for reporting
|
||||
and analysis.
|
||||
|
||||
- Allow parameterized fixtures to specify the ID of the parameters by
|
||||
adding an ids argument to pytest.fixture() and pytest.yield_fixture().
|
||||
Thanks Floris Bruynooghe.
|
||||
|
||||
- fix issue404 by always using the binary xml escape in the junitxml
|
||||
plugin. Thanks Ronny Pfannschmidt.
|
||||
|
||||
- fix issue407: fix addoption docstring to point to argparse instead of
|
||||
optparse. Thanks Daniel D. Wright.
|
||||
|
||||
64
doc/en/announce/release-2.5.2.txt
Normal file
64
doc/en/announce/release-2.5.2.txt
Normal file
@@ -0,0 +1,64 @@
|
||||
pytest-2.5.2: fixes
|
||||
===========================================================================
|
||||
|
||||
pytest is a mature Python testing tool with more than a 1000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
The 2.5.2 release fixes a few bugs with two maybe-bugs remaining and
|
||||
actively being worked on (and waiting for the bug reporter's input).
|
||||
We also have a new contribution guide thanks to Piotr Banaszkiewicz
|
||||
and others.
|
||||
|
||||
See docs at:
|
||||
|
||||
http://pytest.org
|
||||
|
||||
As usual, you can upgrade from pypi via::
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to the following people who contributed to this release:
|
||||
|
||||
Anatoly Bubenkov
|
||||
Ronny Pfannschmidt
|
||||
Floris Bruynooghe
|
||||
Bruno Oliveira
|
||||
Andreas Pelme
|
||||
Jurko Gospodnetić
|
||||
Piotr Banaszkiewicz
|
||||
Simon Liedtke
|
||||
lakka
|
||||
Lukasz Balcerzak
|
||||
Philippe Muller
|
||||
Daniel Hahler
|
||||
|
||||
have fun,
|
||||
holger krekel
|
||||
|
||||
2.5.2
|
||||
-----------------------------------
|
||||
|
||||
- fix issue409 -- better interoperate with cx_freeze by not
|
||||
trying to import from collections.abc which causes problems
|
||||
for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
|
||||
|
||||
- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
|
||||
Thanks Jurko Gospodnetic for the complete PR.
|
||||
|
||||
- fix issue425: mention at end of "py.test -h" that --markers
|
||||
and --fixtures work according to specified test path (or current dir)
|
||||
|
||||
- fix issue413: exceptions with unicode attributes are now printed
|
||||
correctly also on python2 and with pytest-xdist runs. (the fix
|
||||
requires py-1.4.20)
|
||||
|
||||
- copy, cleanup and integrate py.io capture
|
||||
from pylib 1.4.20.dev2 (rev 13d9af95547e)
|
||||
|
||||
- address issue416: clarify docs as to conftest.py loading semantics
|
||||
|
||||
- fix issue429: comparing byte strings with non-ascii chars in assert
|
||||
expressions now work better. Thanks Floris Bruynooghe.
|
||||
|
||||
- make capfd/capsys.capture private, its unused and shouldnt be exposed
|
||||
|
||||
153
doc/en/announce/release-2.6.0.txt
Normal file
153
doc/en/announce/release-2.6.0.txt
Normal file
@@ -0,0 +1,153 @@
|
||||
pytest-2.6.0: shorter tracebacks, new warning system, test runner compat
|
||||
===========================================================================
|
||||
|
||||
pytest is a mature Python testing tool with more than a 1000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
The 2.6.0 release should be drop-in backward compatible to 2.5.2 and
|
||||
fixes a number of bugs and brings some new features, mainly:
|
||||
|
||||
- shorter tracebacks by default: only the first (test function) entry
|
||||
and the last (failure location) entry are shown, the ones between
|
||||
only in "short" format. Use ``--tb=long`` to get back the old
|
||||
behaviour of showing "long" entries everywhere.
|
||||
|
||||
- a new warning system which reports oddities during collection
|
||||
and execution. For example, ignoring collecting Test* classes with an
|
||||
``__init__`` now produces a warning.
|
||||
|
||||
- various improvements to nose/mock/unittest integration
|
||||
|
||||
Note also that 2.6.0 departs with the "zero reported bugs" policy
|
||||
because it has been too hard to keep up with it, unfortunately.
|
||||
Instead we are for now rather bound to work on "upvoted" issues in
|
||||
the https://bitbucket.org/hpk42/pytest/issues?status=new&status=open&sort=-votes
|
||||
issue tracker.
|
||||
|
||||
See docs at:
|
||||
|
||||
http://pytest.org
|
||||
|
||||
As usual, you can upgrade from pypi via::
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed, among them:
|
||||
|
||||
Benjamin Peterson
|
||||
Jurko Gospodnetić
|
||||
Floris Bruynooghe
|
||||
Marc Abramowitz
|
||||
Marc Schlaich
|
||||
Trevor Bekolay
|
||||
Bruno Oliveira
|
||||
Alex Groenholm
|
||||
|
||||
have fun,
|
||||
holger krekel
|
||||
|
||||
2.6.0
|
||||
-----------------------------------
|
||||
|
||||
- fix issue537: Avoid importing old assertion reinterpretation code by default.
|
||||
Thanks Benjamin Peterson.
|
||||
|
||||
- fix issue364: shorten and enhance tracebacks representation by default.
|
||||
The new "--tb=auto" option (default) will only display long tracebacks
|
||||
for the first and last entry. You can get the old behaviour of printing
|
||||
all entries as long entries with "--tb=long". Also short entries by
|
||||
default are now printed very similarly to "--tb=native" ones.
|
||||
|
||||
- fix issue514: teach assertion reinterpretation about private class attributes
|
||||
Thanks Benjamin Peterson.
|
||||
|
||||
- change -v output to include full node IDs of tests. Users can copy
|
||||
a node ID from a test run, including line number, and use it as a
|
||||
positional argument in order to run only a single test.
|
||||
|
||||
- fix issue 475: fail early and comprehensible if calling
|
||||
pytest.raises with wrong exception type.
|
||||
|
||||
- fix issue516: tell in getting-started about current dependencies.
|
||||
|
||||
- cleanup setup.py a bit and specify supported versions. Thanks Jurko
|
||||
Gospodnetic for the PR.
|
||||
|
||||
- change XPASS colour to yellow rather then red when tests are run
|
||||
with -v.
|
||||
|
||||
- fix issue473: work around mock putting an unbound method into a class
|
||||
dict when double-patching.
|
||||
|
||||
- fix issue498: if a fixture finalizer fails, make sure that
|
||||
the fixture is still invalidated.
|
||||
|
||||
- fix issue453: the result of the pytest_assertrepr_compare hook now gets
|
||||
it's newlines escaped so that format_exception does not blow up.
|
||||
|
||||
- internal new warning system: pytest will now produce warnings when
|
||||
it detects oddities in your test collection or execution.
|
||||
Warnings are ultimately sent to a new pytest_logwarning hook which is
|
||||
currently only implemented by the terminal plugin which displays
|
||||
warnings in the summary line and shows more details when -rw (report on
|
||||
warnings) is specified.
|
||||
|
||||
- change skips into warnings for test classes with an __init__ and
|
||||
callables in test modules which look like a test but are not functions.
|
||||
|
||||
- fix issue436: improved finding of initial conftest files from command
|
||||
line arguments by using the result of parse_known_args rather than
|
||||
the previous flaky heuristics. Thanks Marc Abramowitz for tests
|
||||
and initial fixing approaches in this area.
|
||||
|
||||
- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
|
||||
during collection/loading of test modules. Thanks to Marc Schlaich
|
||||
for the complete PR.
|
||||
|
||||
- fix issue490: include pytest_load_initial_conftests in documentation
|
||||
and improve docstring.
|
||||
|
||||
- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
|
||||
if it's triggered ahead of command line parsing.
|
||||
|
||||
- merge PR123: improved integration with mock.patch decorator on tests.
|
||||
|
||||
- fix issue412: messing with stdout/stderr FD-level streams is now
|
||||
captured without crashes.
|
||||
|
||||
- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR.
|
||||
|
||||
- improve example for pytest integration with "python setup.py test"
|
||||
which now has a generic "-a" or "--pytest-args" option where you
|
||||
can pass additional options as a quoted string. Thanks Trevor Bekolay.
|
||||
|
||||
- simplified internal capturing mechanism and made it more robust
|
||||
against tests or setups changing FD1/FD2, also better integrated
|
||||
now with pytest.pdb() in single tests.
|
||||
|
||||
- improvements to pytest's own test-suite leakage detection, courtesy of PRs
|
||||
from Marc Abramowitz
|
||||
|
||||
- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz.
|
||||
|
||||
- fix issue493: don't run tests in doc directory with ``python setup.py test``
|
||||
(use tox -e doctesting for that)
|
||||
|
||||
- fix issue486: better reporting and handling of early conftest loading failures
|
||||
|
||||
- some cleanup and simplification of internal conftest handling.
|
||||
|
||||
- work a bit harder to break reference cycles when catching exceptions.
|
||||
Thanks Jurko Gospodnetic.
|
||||
|
||||
- fix issue443: fix skip examples to use proper comparison. Thanks Alex
|
||||
Groenholm.
|
||||
|
||||
- support nose-style ``__test__`` attribute on modules, classes and
|
||||
functions, including unittest-style Classes. If set to False, the
|
||||
test will not be collected.
|
||||
|
||||
- fix issue512: show "<notset>" for arguments which might not be set
|
||||
in monkeypatch plugin. Improves output in documentation.
|
||||
|
||||
- avoid importing "py.test" (an old alias module for "pytest")
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
.. _apiref:
|
||||
|
||||
py.test reference documentation
|
||||
pytest reference documentation
|
||||
================================================
|
||||
|
||||
.. toctree::
|
||||
|
||||
@@ -10,7 +10,7 @@ The writing and reporting of assertions in tests
|
||||
Asserting with the ``assert`` statement
|
||||
---------------------------------------------------------
|
||||
|
||||
``py.test`` allows you to use the standard python ``assert`` for verifying
|
||||
``pytest`` allows you to use the standard python ``assert`` for verifying
|
||||
expectations and values in Python tests. For example, you can write the
|
||||
following::
|
||||
|
||||
@@ -26,7 +26,7 @@ you will see the return value of the function call::
|
||||
|
||||
$ py.test test_assert1.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
test_assert1.py F
|
||||
@@ -42,7 +42,7 @@ you will see the return value of the function call::
|
||||
test_assert1.py:5: AssertionError
|
||||
========================= 1 failed in 0.01 seconds =========================
|
||||
|
||||
py.test has support for showing the values of the most common subexpressions
|
||||
``pytest`` has support for showing the values of the most common subexpressions
|
||||
including calls, attributes, comparisons, and binary and unary
|
||||
operators. (See :ref:`tbreportdemo`). This allows you to use the
|
||||
idiomatic python constructs without boilerplate code while not losing
|
||||
@@ -102,7 +102,7 @@ Making use of context-sensitive comparisons
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
py.test has rich support for providing context-sensitive information
|
||||
``pytest`` has rich support for providing context-sensitive information
|
||||
when it encounters comparisons. For example::
|
||||
|
||||
# content of test_assert2.py
|
||||
@@ -116,7 +116,7 @@ if you run this module::
|
||||
|
||||
$ py.test test_assert2.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
test_assert2.py F
|
||||
@@ -175,21 +175,21 @@ now, given this test module::
|
||||
f2 = Foo(2)
|
||||
assert f1 == f2
|
||||
|
||||
you can run the test module and get the custom output defined in
|
||||
you can run the test module and get the custom output defined in
|
||||
the conftest file::
|
||||
|
||||
$ py.test -q test_foocompare.py
|
||||
F
|
||||
================================= FAILURES =================================
|
||||
_______________________________ test_compare _______________________________
|
||||
|
||||
|
||||
def test_compare():
|
||||
f1 = Foo(1)
|
||||
f2 = Foo(2)
|
||||
> assert f1 == f2
|
||||
E assert Comparing Foo instances:
|
||||
E vals: 1 != 2
|
||||
|
||||
|
||||
test_foocompare.py:8: AssertionError
|
||||
1 failed in 0.01 seconds
|
||||
|
||||
@@ -205,33 +205,33 @@ Advanced assertion introspection
|
||||
Reporting details about a failing assertion is achieved either by rewriting
|
||||
assert statements before they are run or re-evaluating the assert expression and
|
||||
recording the intermediate values. Which technique is used depends on the
|
||||
location of the assert, py.test's configuration, and Python version being used
|
||||
to run py.test. Note that for assert statements with a manually provided
|
||||
location of the assert, ``pytest`` configuration, and Python version being used
|
||||
to run ``pytest``. Note that for assert statements with a manually provided
|
||||
message, i.e. ``assert expr, message``, no assertion introspection takes place
|
||||
and the manually provided message will be rendered in tracebacks.
|
||||
|
||||
By default, if the Python version is greater than or equal to 2.6, py.test
|
||||
By default, if the Python version is greater than or equal to 2.6, ``pytest``
|
||||
rewrites assert statements in test modules. Rewritten assert statements put
|
||||
introspection information into the assertion failure message. py.test only
|
||||
introspection information into the assertion failure message. ``pytest`` only
|
||||
rewrites test modules directly discovered by its test collection process, so
|
||||
asserts in supporting modules which are not themselves test modules will not be
|
||||
rewritten.
|
||||
|
||||
.. note::
|
||||
|
||||
py.test rewrites test modules on import. It does this by using an import hook
|
||||
to write a new pyc files. Most of the time this works transparently. However,
|
||||
if you are messing with import yourself, the import hook may interfere. If
|
||||
this is the case, simply use ``--assert=reinterp`` or
|
||||
``pytest`` rewrites test modules on import. It does this by using an import
|
||||
hook to write a new pyc files. Most of the time this works transparently.
|
||||
However, if you are messing with import yourself, the import hook may
|
||||
interfere. If this is the case, simply use ``--assert=reinterp`` or
|
||||
``--assert=plain``. Additionally, rewriting will fail silently if it cannot
|
||||
write new pycs, i.e. in a read-only filesystem or a zipfile.
|
||||
|
||||
If an assert statement has not been rewritten or the Python version is less than
|
||||
2.6, py.test falls back on assert reinterpretation. In assert reinterpretation,
|
||||
py.test walks the frame of the function containing the assert statement to
|
||||
discover sub-expression results of the failing assert statement. You can force
|
||||
py.test to always use assertion reinterpretation by passing the
|
||||
``--assert=reinterp`` option.
|
||||
2.6, ``pytest`` falls back on assert reinterpretation. In assert
|
||||
reinterpretation, ``pytest`` walks the frame of the function containing the
|
||||
assert statement to discover sub-expression results of the failing assert
|
||||
statement. You can force ``pytest`` to always use assertion reinterpretation by
|
||||
passing the ``--assert=reinterp`` option.
|
||||
|
||||
Assert reinterpretation has a caveat not present with assert rewriting: If
|
||||
evaluating the assert expression has side effects you may get a warning that the
|
||||
@@ -250,7 +250,7 @@ easy to rewrite the assertion and avoid any trouble::
|
||||
|
||||
All assert introspection can be turned off by passing ``--assert=plain``.
|
||||
|
||||
For further information, Benjamin Peterson wrote up `Behind the scenes of py.test's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
|
||||
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
Add assert rewriting as an alternate introspection technique.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Setting up bash completion
|
||||
==========================
|
||||
|
||||
When using bash as your shell, ``py.test`` can use argcomplete
|
||||
When using bash as your shell, ``pytest`` can use argcomplete
|
||||
(https://argcomplete.readthedocs.org/) for auto-completion.
|
||||
For this ``argcomplete`` needs to be installed **and** enabled.
|
||||
|
||||
@@ -16,11 +16,11 @@ For global activation of all argcomplete enabled python applications run::
|
||||
|
||||
sudo activate-global-python-argcomplete
|
||||
|
||||
For permanent (but not global) ``py.test`` activation, use::
|
||||
For permanent (but not global) ``pytest`` activation, use::
|
||||
|
||||
register-python-argcomplete py.test >> ~/.bashrc
|
||||
|
||||
For one-time activation of argcomplete for ``py.test`` only, use::
|
||||
For one-time activation of argcomplete for ``pytest`` only, use::
|
||||
|
||||
eval "$(register-python-argcomplete py.test)"
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ You can ask for available builtin or project-custom
|
||||
|
||||
capfd
|
||||
enables capturing of writes to file descriptors 1 and 2 and makes
|
||||
captured output available via ``capsys.readouterr()`` method calls
|
||||
captured output available via ``capfd.readouterr()`` method calls
|
||||
which return a ``(out, err)`` tuple.
|
||||
|
||||
monkeypatch
|
||||
|
||||
@@ -23,7 +23,7 @@ a test.
|
||||
Setting capturing methods or disabling capturing
|
||||
-------------------------------------------------
|
||||
|
||||
There are two ways in which ``py.test`` can perform capturing:
|
||||
There are two ways in which ``pytest`` can perform capturing:
|
||||
|
||||
* file descriptor (FD) level capturing (default): All writes going to the
|
||||
operating system file descriptors 1 and 2 will be captured.
|
||||
@@ -49,7 +49,7 @@ One primary benefit of the default capturing of stdout/stderr output
|
||||
is that you can use print statements for debugging::
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
|
||||
def setup_function(function):
|
||||
print ("setting up %s" % function)
|
||||
|
||||
@@ -64,7 +64,7 @@ of the failing function and hide the other one::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_module.py .F
|
||||
@@ -77,18 +77,16 @@ of the failing function and hide the other one::
|
||||
E assert False
|
||||
|
||||
test_module.py:9: AssertionError
|
||||
----------------------------- Captured stdout ------------------------------
|
||||
setting up <function test_func2 at 0x282d2a8>
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
setting up <function test_func2 at 0x2abe0d7241b8>
|
||||
==================== 1 failed, 1 passed in 0.01 seconds ====================
|
||||
|
||||
Accessing captured output from a test function
|
||||
---------------------------------------------------
|
||||
|
||||
The :ref:`funcarg mechanism` allows test function a very easy
|
||||
way to access the captured output by simply using the names
|
||||
``capsys`` or ``capfd`` in the test function signature. Here
|
||||
is an example test function that performs some output related
|
||||
checks::
|
||||
The ``capsys`` and ``capfd`` fixtures allow to access stdout/stderr
|
||||
output created during test execution. Here is an example test function
|
||||
that performs some output related checks::
|
||||
|
||||
def test_myoutput(capsys): # or use "capfd" for fd-level
|
||||
print ("hello")
|
||||
@@ -105,11 +103,13 @@ and capturing will be continued. After the test
|
||||
function finishes the original streams will
|
||||
be restored. Using ``capsys`` this way frees your
|
||||
test from having to care about setting/resetting
|
||||
output streams and also interacts well with py.test's
|
||||
output streams and also interacts well with pytest's
|
||||
own per-test capturing.
|
||||
|
||||
If you want to capture on ``fd`` level you can use
|
||||
If you want to capture on filedescriptor level you can use
|
||||
the ``capfd`` function argument which offers the exact
|
||||
same interface.
|
||||
same interface but allows to also capture output from
|
||||
libraries or subprocesses that directly write to operating
|
||||
system level output streams (FD1 and FD2).
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
# The short X.Y version.
|
||||
version = "2.4.2"
|
||||
release = "2.4.2"
|
||||
version = "2.6"
|
||||
release = "2.6.0"
|
||||
|
||||
import sys, os
|
||||
|
||||
@@ -54,7 +54,7 @@ master_doc = 'contents'
|
||||
|
||||
# General information about the project.
|
||||
project = u'pytest'
|
||||
copyright = u'2012, holger krekel'
|
||||
copyright = u'2014, holger krekel'
|
||||
|
||||
|
||||
|
||||
@@ -105,14 +105,19 @@ pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'sphinxdoc'
|
||||
html_theme = 'flask'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {}
|
||||
html_theme_options = {
|
||||
'index_logo': None
|
||||
}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
@@ -126,12 +131,12 @@ html_short_title = "pytest-%s" % release
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
html_logo = "img/pytest1.png"
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
html_favicon = "img/pytest1favi.ico"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
@@ -150,6 +155,23 @@ html_static_path = ['_static']
|
||||
#html_sidebars = {}
|
||||
#html_sidebars = {'index': 'indexsidebar.html'}
|
||||
|
||||
html_sidebars = {
|
||||
'index': [
|
||||
'sidebarintro.html',
|
||||
'globaltoc.html',
|
||||
'links.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html'
|
||||
],
|
||||
'**': [
|
||||
'globaltoc.html',
|
||||
'relations.html',
|
||||
'links.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html'
|
||||
]
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
@@ -198,7 +220,7 @@ htmlhelp_basename = 'pytestdoc'
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('contents', 'pytest.tex', u'pytest Documentation',
|
||||
u'holger krekel, http://merlinux.eu', 'manual'),
|
||||
u'holger krekel, trainer and consultant, http://merlinux.eu', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -240,7 +262,7 @@ man_pages = [
|
||||
epub_title = u'pytest'
|
||||
epub_author = u'holger krekel at merlinux eu'
|
||||
epub_publisher = u'holger krekel at merlinux eu'
|
||||
epub_copyright = u'2012, holger krekel et alii'
|
||||
epub_copyright = u'2013, holger krekel et alii'
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
|
||||
@@ -18,6 +18,9 @@ Contact channels
|
||||
|
||||
- `pytest-commit at python.org (mailing list)`_: for commits and new issues
|
||||
|
||||
- :doc:`contribution guide <contributing>` for help on submitting pull
|
||||
requests to bitbucket (including using git via gitifyhg).
|
||||
|
||||
- #pylib on irc.freenode.net IRC channel for random questions.
|
||||
|
||||
- private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
|
||||
.. note::
|
||||
|
||||
`improving your automated testing with pytest <https://ep2014.europython.eu/en/schedule/sessions/92/>`_, July 25th 2014, Berlin, Germany
|
||||
|
||||
`professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 24-26th November 2014, Freiburg, Germany
|
||||
|
||||
.. _toc:
|
||||
|
||||
Full pytest documentation
|
||||
@@ -14,9 +20,10 @@ Full pytest documentation
|
||||
overview
|
||||
apiref
|
||||
plugins
|
||||
plugins_index/index
|
||||
example/index
|
||||
talks
|
||||
develop
|
||||
contributing
|
||||
funcarg_compare.txt
|
||||
announce/index
|
||||
|
||||
|
||||
3
doc/en/contributing.txt
Normal file
3
doc/en/contributing.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
.. _contributing:
|
||||
|
||||
.. include:: ../../CONTRIBUTING.rst
|
||||
@@ -17,7 +17,7 @@ which were registered by installed plugins.
|
||||
How test configuration is read from configuration INI-files
|
||||
-------------------------------------------------------------
|
||||
|
||||
py.test searches for the first matching ini-style configuration file
|
||||
``pytest`` searches for the first matching ini-style configuration file
|
||||
in the directories of command line argument and the directories above.
|
||||
It looks for file basenames in this order::
|
||||
|
||||
@@ -26,8 +26,8 @@ It looks for file basenames in this order::
|
||||
setup.cfg
|
||||
|
||||
Searching stops when the first ``[pytest]`` section is found in any of
|
||||
these files. There is no merging of configuration values from multiple
|
||||
files. Example::
|
||||
these files. There is no merging of configuration values from multiple
|
||||
files. Example::
|
||||
|
||||
py.test path/to/testdir
|
||||
|
||||
@@ -41,7 +41,7 @@ will look in the following dirs for a config file::
|
||||
path/to/setup.cfg
|
||||
... # up until root of filesystem
|
||||
|
||||
If argument is provided to a py.test run, the current working directory
|
||||
If argument is provided to a ``pytest`` run, the current working directory
|
||||
is used to start the search.
|
||||
|
||||
.. _`how to change command line options defaults`:
|
||||
@@ -51,7 +51,7 @@ How to change command line options defaults
|
||||
------------------------------------------------
|
||||
|
||||
It can be tedious to type the same series of command line options
|
||||
every time you use py.test . For example, if you always want to see
|
||||
every time you use ``pytest``. For example, if you always want to see
|
||||
detailed info on skipped and xfailed tests, as well as have terser "dot"
|
||||
progress output, you can write it into a configuration file::
|
||||
|
||||
@@ -60,7 +60,7 @@ progress output, you can write it into a configuration file::
|
||||
[pytest]
|
||||
addopts = -rsxX -q
|
||||
|
||||
From now on, running ``py.test`` will add the specified options.
|
||||
From now on, running ``pytest`` will add the specified options.
|
||||
|
||||
Builtin configuration file options
|
||||
----------------------------------------------
|
||||
@@ -97,15 +97,15 @@ Builtin configuration file options
|
||||
[seq] matches any character in seq
|
||||
[!seq] matches any char not in seq
|
||||
|
||||
Default patterns are ``.* _darcs CVS {args}``. Setting a ``norecursedir``
|
||||
replaces the default. Here is an example of how to avoid
|
||||
certain directories::
|
||||
Default patterns are ``'.*', 'CVS', '_darcs', '{arch}', '*.egg'``.
|
||||
Setting a ``norecursedirs`` replaces the default. Here is an example of
|
||||
how to avoid certain directories::
|
||||
|
||||
# content of setup.cfg
|
||||
[pytest]
|
||||
norecursedirs = .svn _build tmp*
|
||||
|
||||
This would tell py.test to not look into typical subversion or
|
||||
This would tell ``pytest`` to not look into typical subversion or
|
||||
sphinx-build directories or into any ``tmp`` prefixed directory.
|
||||
|
||||
.. confval:: python_files
|
||||
@@ -121,6 +121,8 @@ Builtin configuration file options
|
||||
.. confval:: python_functions
|
||||
|
||||
One or more name prefixes determining which test functions
|
||||
and methods are considered as test modules.
|
||||
and methods are considered as test modules. Note that this
|
||||
has no effect on methods that live on a ``unittest.TestCase``
|
||||
derived class.
|
||||
|
||||
See :ref:`change naming conventions` for examples.
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
=================================================
|
||||
Feedback and contribute to py.test
|
||||
=================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
contact.txt
|
||||
|
||||
.. _checkout:
|
||||
|
||||
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/pytest/
|
||||
|
||||
You can also go to the python package index and
|
||||
download and unpack a TAR file::
|
||||
|
||||
http://pypi.python.org/pypi/pytest/
|
||||
|
||||
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.
|
||||
|
||||
If this command complains that it could not find the required version
|
||||
of "py" then you need to use the development pypi repository::
|
||||
|
||||
python setup.py develop -i http://pypi.testrun.org
|
||||
|
||||
|
||||
.. include:: links.inc
|
||||
@@ -44,15 +44,19 @@ then you can just invoke ``py.test`` without command line options::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
mymodule.py .
|
||||
|
||||
========================= 1 passed in 0.01 seconds =========================
|
||||
========================= 1 passed in 0.04 seconds =========================
|
||||
|
||||
It is possible to use fixtures using the ``getfixture`` helper::
|
||||
|
||||
# content of example.rst
|
||||
>>> tmp = getfixture('tmpdir')
|
||||
>>> ...
|
||||
>>>
|
||||
|
||||
Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
|
||||
when executing text doctest files.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from py.test import raises
|
||||
from pytest import raises
|
||||
import py
|
||||
|
||||
def otherfunc(a,b):
|
||||
|
||||
@@ -21,6 +21,9 @@ You can "mark" a test function with custom metadata like this::
|
||||
pass
|
||||
def test_another():
|
||||
pass
|
||||
class TestClass:
|
||||
def test_method(self):
|
||||
pass
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
@@ -28,26 +31,82 @@ You can then restrict a test run to only run tests marked with ``webtest``::
|
||||
|
||||
$ py.test -v -m webtest
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 3 items
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_server.py:3: test_send_http PASSED
|
||||
test_server.py@3::test_send_http PASSED
|
||||
|
||||
=================== 2 tests deselected by "-m 'webtest'" ===================
|
||||
================== 1 passed, 2 deselected in 0.01 seconds ==================
|
||||
=================== 3 tests deselected by "-m 'webtest'" ===================
|
||||
================== 1 passed, 3 deselected in 0.01 seconds ==================
|
||||
|
||||
Or the inverse, running all tests except the webtest ones::
|
||||
|
||||
|
||||
$ py.test -v -m "not webtest"
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 3 items
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_server.py:6: test_something_quick PASSED
|
||||
test_server.py:8: test_another PASSED
|
||||
test_server.py@6::test_something_quick PASSED
|
||||
test_server.py@8::test_another PASSED
|
||||
test_server.py@11::TestClass::test_method PASSED
|
||||
|
||||
================= 1 tests deselected by "-m 'not webtest'" =================
|
||||
================== 2 passed, 1 deselected in 0.01 seconds ==================
|
||||
================== 3 passed, 1 deselected in 0.01 seconds ==================
|
||||
|
||||
Selecing tests based on their node ID
|
||||
-------------------------------------
|
||||
|
||||
You can provide one or more :ref:`node IDs <node-id>` as positional
|
||||
arguments to select only specified tests. This makes it easy to select
|
||||
tests based on their module, class, method, or function name::
|
||||
|
||||
$ py.test -v test_server.py::TestClass::test_method
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 5 items
|
||||
|
||||
test_server.py@11::TestClass::test_method PASSED
|
||||
|
||||
========================= 1 passed in 0.01 seconds =========================
|
||||
|
||||
You can also select on the class::
|
||||
|
||||
$ py.test -v test_server.py::TestClass
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_server.py@11::TestClass::test_method PASSED
|
||||
|
||||
========================= 1 passed in 0.01 seconds =========================
|
||||
|
||||
Or select multiple nodes::
|
||||
|
||||
$ py.test -v test_server.py::TestClass test_server.py::test_send_http
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 8 items
|
||||
|
||||
test_server.py@11::TestClass::test_method PASSED
|
||||
test_server.py@3::test_send_http PASSED
|
||||
|
||||
========================= 2 passed in 0.01 seconds =========================
|
||||
|
||||
.. _node-id:
|
||||
|
||||
.. note::
|
||||
|
||||
Node IDs are of the form ``module.py::class::method`` or
|
||||
``module.py::function``. Node IDs control which tests are
|
||||
collected, so ``module.py::class`` will select all test methods
|
||||
on the class. Nodes are also created for each parameter of a
|
||||
parametrized fixture or test, so selecting a parametrized test
|
||||
must include the parameter value, e.g.
|
||||
``module.py::function[param]``.
|
||||
|
||||
Node IDs for failing tests are displayed in the test summary info
|
||||
when running py.test with the ``-rf`` option. You can also
|
||||
construct Node IDs from the output of ``py.test --collectonly``.
|
||||
|
||||
Using ``-k expr`` to select tests based on their name
|
||||
-------------------------------------------------------
|
||||
@@ -61,39 +120,51 @@ select tests based on their names::
|
||||
|
||||
$ py.test -v -k http # running with the above defined example module
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 3 items
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_server.py:3: test_send_http PASSED
|
||||
test_server.py@3::test_send_http PASSED
|
||||
|
||||
====================== 2 tests deselected by '-khttp' ======================
|
||||
================== 1 passed, 2 deselected in 0.01 seconds ==================
|
||||
====================== 3 tests deselected by '-khttp' ======================
|
||||
================== 1 passed, 3 deselected in 0.01 seconds ==================
|
||||
|
||||
And you can also run all tests except the ones that match the keyword::
|
||||
|
||||
$ py.test -k "not send_http" -v
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 3 items
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_server.py:6: test_something_quick PASSED
|
||||
test_server.py:8: test_another PASSED
|
||||
test_server.py@6::test_something_quick PASSED
|
||||
test_server.py@8::test_another PASSED
|
||||
test_server.py@11::TestClass::test_method PASSED
|
||||
|
||||
================= 1 tests deselected by '-knot send_http' ==================
|
||||
================== 2 passed, 1 deselected in 0.01 seconds ==================
|
||||
================== 3 passed, 1 deselected in 0.01 seconds ==================
|
||||
|
||||
Or to select "http" and "quick" tests::
|
||||
|
||||
$ py.test -k "http or quick" -v
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 3 items
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_server.py:3: test_send_http PASSED
|
||||
test_server.py:6: test_something_quick PASSED
|
||||
test_server.py@3::test_send_http PASSED
|
||||
test_server.py@6::test_something_quick PASSED
|
||||
|
||||
================= 1 tests deselected by '-khttp or quick' ==================
|
||||
================== 2 passed, 1 deselected in 0.01 seconds ==================
|
||||
================= 2 tests deselected by '-khttp or quick' ==================
|
||||
================== 2 passed, 2 deselected in 0.01 seconds ==================
|
||||
|
||||
.. note::
|
||||
|
||||
If you are using expressions such as "X and Y" then both X and Y
|
||||
need to be simple non-keyword names. For example, "pass" or "from"
|
||||
will result in SyntaxErrors because "-k" evaluates the expression.
|
||||
|
||||
However, if the "-k" argument is a simple string, no such restrictions
|
||||
apply. Also "-k 'not STRING'" has no restrictions. You can also
|
||||
specify numbers like "-k 1.3" to match tests which are parametrized
|
||||
with the float "1.3".
|
||||
|
||||
Registering markers
|
||||
-------------------------------------
|
||||
@@ -118,7 +189,7 @@ You can ask which markers exist for your test suite - the list includes our just
|
||||
|
||||
@pytest.mark.xfail(condition, reason=None, run=True): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. See http://pytest.org/latest/skipping.html
|
||||
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. see http://pytest.org/latest/parametrize.html for more info and examples.
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
|
||||
|
||||
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
|
||||
|
||||
@@ -139,8 +210,8 @@ For an example on how to add and work with markers from a plugin, see
|
||||
* asking for existing markers via ``py.test --markers`` gives good output
|
||||
|
||||
* typos in function markers are treated as an error if you use
|
||||
the ``--strict`` option. Later versions of py.test are probably
|
||||
going to treat non-registered markers as an error.
|
||||
the ``--strict`` option. Future versions of ``pytest`` are probably
|
||||
going to start treating non-registered markers as errors at some point.
|
||||
|
||||
.. _`scoped-marking`:
|
||||
|
||||
@@ -255,18 +326,18 @@ the test needs::
|
||||
|
||||
$ py.test -E stage2
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
test_someenv.py s
|
||||
|
||||
======================== 1 skipped in 0.01 seconds =========================
|
||||
|
||||
|
||||
and here is one that specifies exactly the environment needed::
|
||||
|
||||
$ py.test -E stage1
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
test_someenv.py .
|
||||
@@ -282,7 +353,7 @@ The ``--markers`` option always gives you a list of available markers::
|
||||
|
||||
@pytest.mark.xfail(condition, reason=None, run=True): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. See http://pytest.org/latest/skipping.html
|
||||
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. see http://pytest.org/latest/parametrize.html for more info and examples.
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
|
||||
|
||||
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
|
||||
|
||||
@@ -290,7 +361,7 @@ The ``--markers`` option always gives you a list of available markers::
|
||||
|
||||
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
|
||||
|
||||
|
||||
|
||||
Reading markers which were set from multiple places
|
||||
----------------------------------------------------
|
||||
|
||||
@@ -326,7 +397,7 @@ test function. From a conftest file we can read it like this::
|
||||
|
||||
Let's run this without capturing output and see what we get::
|
||||
|
||||
$ py.test -q -s
|
||||
$ py.test -q -s
|
||||
glob args=('function',) kwargs={'x': 3}
|
||||
glob args=('class',) kwargs={'x': 2}
|
||||
glob args=('module',) kwargs={'x': 1}
|
||||
@@ -341,7 +412,7 @@ marking platform specific tests with pytest
|
||||
Consider you have a test suite which marks tests for particular platforms,
|
||||
namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you
|
||||
also have tests that run on all platforms and have no specific
|
||||
marker. If you now want to have a way to only run the tests
|
||||
marker. If you now want to have a way to only run the tests
|
||||
for your particular platform, you could use the following plugin::
|
||||
|
||||
# content of conftest.py
|
||||
@@ -384,12 +455,12 @@ then you will see two test skipped and two executed tests as expected::
|
||||
|
||||
$ py.test -rs # this option reports skip reasons
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
|
||||
test_plat.py s.s.
|
||||
========================= short test summary info ==========================
|
||||
SKIP [2] /tmp/doc-exec-598/conftest.py:12: cannot run on platform linux2
|
||||
SKIP [2] /tmp/doc-exec-142/conftest.py:12: cannot run on platform linux2
|
||||
|
||||
=================== 2 passed, 2 skipped in 0.01 seconds ====================
|
||||
|
||||
@@ -397,7 +468,7 @@ Note that if you specify a platform via the marker-command line option like this
|
||||
|
||||
$ py.test -m linux2
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
|
||||
test_plat.py .
|
||||
@@ -405,7 +476,7 @@ Note that if you specify a platform via the marker-command line option like this
|
||||
=================== 3 tests deselected by "-m 'linux2'" ====================
|
||||
================== 1 passed, 3 deselected in 0.01 seconds ==================
|
||||
|
||||
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
|
||||
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
|
||||
|
||||
Automatically adding markers based on test names
|
||||
--------------------------------------------------------
|
||||
@@ -424,7 +495,7 @@ at this test module::
|
||||
|
||||
def test_interface_complex():
|
||||
assert 0
|
||||
|
||||
|
||||
def test_event_simple():
|
||||
assert 0
|
||||
|
||||
@@ -435,7 +506,7 @@ We want to dynamically define two markers and can do it in a
|
||||
``conftest.py`` plugin::
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
|
||||
import pytest
|
||||
def pytest_collection_modifyitems(items):
|
||||
for item in items:
|
||||
@@ -448,7 +519,7 @@ We can now use the ``-m option`` to select one set::
|
||||
|
||||
$ py.test -m interface --tb=short
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
|
||||
test_module.py FF
|
||||
@@ -456,12 +527,12 @@ We can now use the ``-m option`` to select one set::
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_interface_simple ___________________________
|
||||
test_module.py:3: in test_interface_simple
|
||||
> assert 0
|
||||
E assert 0
|
||||
assert 0
|
||||
E assert 0
|
||||
__________________________ test_interface_complex __________________________
|
||||
test_module.py:6: in test_interface_complex
|
||||
> assert 0
|
||||
E assert 0
|
||||
assert 0
|
||||
E assert 0
|
||||
================== 2 tests deselected by "-m 'interface'" ==================
|
||||
================== 2 failed, 2 deselected in 0.01 seconds ==================
|
||||
|
||||
@@ -469,7 +540,7 @@ or to select both "event" and "interface" tests::
|
||||
|
||||
$ py.test -m "interface or event" --tb=short
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
|
||||
test_module.py FFF
|
||||
@@ -477,15 +548,15 @@ or to select both "event" and "interface" tests::
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_interface_simple ___________________________
|
||||
test_module.py:3: in test_interface_simple
|
||||
> assert 0
|
||||
E assert 0
|
||||
assert 0
|
||||
E assert 0
|
||||
__________________________ test_interface_complex __________________________
|
||||
test_module.py:6: in test_interface_complex
|
||||
> assert 0
|
||||
E assert 0
|
||||
assert 0
|
||||
E assert 0
|
||||
____________________________ test_event_simple _____________________________
|
||||
test_module.py:9: in test_event_simple
|
||||
> assert 0
|
||||
E assert 0
|
||||
assert 0
|
||||
E assert 0
|
||||
============= 1 tests deselected by "-m 'interface or event'" ==============
|
||||
================== 3 failed, 1 deselected in 0.02 seconds ==================
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
module containing a parametrized tests testing cross-python
|
||||
serialization via the pickle module.
|
||||
"""
|
||||
import py, pytest
|
||||
import py
|
||||
import pytest
|
||||
|
||||
pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8']
|
||||
@pytest.fixture(params=pythonlist)
|
||||
@@ -18,7 +19,7 @@ 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,))
|
||||
pytest.skip("%r not found" %(version,))
|
||||
self.picklefile = picklefile
|
||||
def dumps(self, obj):
|
||||
dumpfile = self.picklefile.dirpath("dump.py")
|
||||
|
||||
@@ -27,7 +27,7 @@ now execute the test specification::
|
||||
|
||||
nonpython $ py.test test_simple.yml
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_simple.yml .F
|
||||
@@ -56,11 +56,11 @@ consulted when reporting in ``verbose`` mode::
|
||||
|
||||
nonpython $ py.test -v
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_simple.yml:1: usecase: ok PASSED
|
||||
test_simple.yml:1: usecase: hello FAILED
|
||||
test_simple.yml@1::usecase: ok PASSED
|
||||
test_simple.yml@1::usecase: hello FAILED
|
||||
|
||||
================================= FAILURES =================================
|
||||
______________________________ usecase: hello ______________________________
|
||||
@@ -74,7 +74,7 @@ interesting to just look at the collection tree::
|
||||
|
||||
nonpython $ py.test --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
<YamlFile 'test_simple.yml'>
|
||||
<YamlItem 'ok'>
|
||||
|
||||
@@ -6,7 +6,7 @@ Parametrizing tests
|
||||
|
||||
.. currentmodule:: _pytest.python
|
||||
|
||||
py.test allows to easily parametrize test functions.
|
||||
``pytest`` allows to easily parametrize test functions.
|
||||
For basic docs, see :ref:`parametrize-basics`.
|
||||
|
||||
In the following we provide some examples using
|
||||
@@ -55,13 +55,13 @@ let's run the full monty::
|
||||
....F
|
||||
================================= FAILURES =================================
|
||||
_____________________________ test_compute[4] ______________________________
|
||||
|
||||
|
||||
param1 = 4
|
||||
|
||||
|
||||
def test_compute(param1):
|
||||
> assert param1 < 4
|
||||
E assert 4 < 4
|
||||
|
||||
|
||||
test_compute.py:3: AssertionError
|
||||
1 failed, 4 passed in 0.01 seconds
|
||||
|
||||
@@ -106,7 +106,7 @@ this is a fully self-contained example which you can run with::
|
||||
|
||||
$ py.test test_scenarios.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
|
||||
test_scenarios.py ....
|
||||
@@ -118,7 +118,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
|
||||
|
||||
$ py.test --collect-only test_scenarios.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
<Module 'test_scenarios.py'>
|
||||
<Class 'TestSampleWithScenarios'>
|
||||
@@ -182,7 +182,7 @@ Let's first see how it looks like at collection time::
|
||||
|
||||
$ py.test test_backends.py --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
<Module 'test_backends.py'>
|
||||
<Function 'test_db_initialized[d1]'>
|
||||
@@ -197,7 +197,7 @@ And then when we run the test::
|
||||
================================= FAILURES =================================
|
||||
_________________________ test_db_initialized[d2] __________________________
|
||||
|
||||
db = <conftest.DB2 instance at 0x2dbd950>
|
||||
db = <conftest.DB2 instance at 0x2b45c2b12050>
|
||||
|
||||
def test_db_initialized(db):
|
||||
# a dummy test
|
||||
@@ -253,14 +253,14 @@ argument sets to use for each test function. Let's run it::
|
||||
================================= FAILURES =================================
|
||||
________________________ TestClass.test_equals[1-2] ________________________
|
||||
|
||||
self = <test_parametrize.TestClass instance at 0x258a6c8>, a = 1, b = 2
|
||||
self = <test_parametrize.TestClass instance at 0x2acd519c6200>, a = 1, b = 2
|
||||
|
||||
def test_equals(self, a, b):
|
||||
> assert a == b
|
||||
E assert 1 == 2
|
||||
|
||||
test_parametrize.py:18: AssertionError
|
||||
1 failed, 2 passed in 0.02 seconds
|
||||
1 failed, 2 passed in 0.01 seconds
|
||||
|
||||
Indirect parametrization with multiple fixtures
|
||||
--------------------------------------------------------------
|
||||
@@ -279,10 +279,12 @@ is to be run with different sets of arguments for its three arguments:
|
||||
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
|
||||
|
||||
. $ py.test -rs -q multipython.py
|
||||
............sss............sss............sss............ssssssssssssssssss
|
||||
ssssssssssssssssssssssssssssssssssss......sssssssss......ssssssssssssssssss
|
||||
========================= short test summary info ==========================
|
||||
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found
|
||||
48 passed, 27 skipped in 1.37 seconds
|
||||
SKIP [21] /home/hpk/p/pytest/doc/en/example/multipython.py:22: 'python2.4' not found
|
||||
SKIP [21] /home/hpk/p/pytest/doc/en/example/multipython.py:22: 'python2.8' not found
|
||||
SKIP [21] /home/hpk/p/pytest/doc/en/example/multipython.py:22: 'python2.5' not found
|
||||
12 passed, 63 skipped in 0.66 seconds
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
--------------------------------------------------------------------
|
||||
@@ -290,7 +292,7 @@ Indirect parametrization of optional implementations/imports
|
||||
If you want to compare the outcomes of several implementations of a given
|
||||
API, you can write test functions that receive the already imported implementations
|
||||
and get skipped in case the implementation is not importable/available. Let's
|
||||
say we have a "base" implementation and the other (possibly optimized ones)
|
||||
say we have a "base" implementation and the other (possibly optimized ones)
|
||||
need to provide similar results::
|
||||
|
||||
# content of conftest.py
|
||||
@@ -329,12 +331,12 @@ If you run this with reporting for skips enabled::
|
||||
|
||||
$ py.test -rs test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_module.py s.
|
||||
test_module.py .s
|
||||
========================= short test summary info ==========================
|
||||
SKIP [1] /tmp/doc-exec-600/conftest.py:10: could not import 'opt2'
|
||||
SKIP [1] /tmp/doc-exec-144/conftest.py:10: could not import 'opt2'
|
||||
|
||||
=================== 1 passed, 1 skipped in 0.01 seconds ====================
|
||||
|
||||
@@ -342,13 +344,13 @@ You'll see that we don't have a ``opt2`` module and thus the second test run
|
||||
of our ``test_func1`` was skipped. A few notes:
|
||||
|
||||
- the fixture functions in the ``conftest.py`` file are "session-scoped" because we
|
||||
don't need to import more than once
|
||||
don't need to import more than once
|
||||
|
||||
- if you have multiple test functions and a skipped import, you will see
|
||||
the ``[1]`` count increasing in the report
|
||||
|
||||
- you can put :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>` style
|
||||
parametrization on the test functions to parametrize input/output
|
||||
parametrization on the test functions to parametrize input/output
|
||||
values as well.
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ You can set the :confval:`norecursedirs` option in an ini-file, for example your
|
||||
[pytest]
|
||||
norecursedirs = .svn _build tmp*
|
||||
|
||||
This would tell py.test to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
|
||||
This would tell ``pytest`` to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
|
||||
|
||||
.. _`change naming conventions`:
|
||||
|
||||
@@ -28,7 +28,7 @@ the :confval:`python_files`, :confval:`python_classes` and
|
||||
python_classes=Check
|
||||
python_functions=check
|
||||
|
||||
This would make py.test look for ``check_`` prefixes in
|
||||
This would make ``pytest`` look for ``check_`` prefixes in
|
||||
Python filenames, ``Check`` prefixes in classes and ``check`` prefixes
|
||||
in functions and classes. For example, if we have::
|
||||
|
||||
@@ -43,7 +43,7 @@ then the test collection looks like this::
|
||||
|
||||
$ py.test --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
<Module 'check_myapp.py'>
|
||||
<Class 'CheckMyApp'>
|
||||
@@ -53,10 +53,16 @@ then the test collection looks like this::
|
||||
|
||||
============================= in 0.01 seconds =============================
|
||||
|
||||
.. note::
|
||||
|
||||
the ``python_functions`` and ``python_classes`` has no effect
|
||||
for ``unittest.TestCase`` test discovery because pytest delegates
|
||||
detection of test case methods to unittest code.
|
||||
|
||||
Interpreting cmdline arguments as Python packages
|
||||
-----------------------------------------------------
|
||||
|
||||
You can use the ``--pyargs`` option to make py.test try
|
||||
You can use the ``--pyargs`` option to make ``pytest`` try
|
||||
interpreting arguments as python package names, deriving
|
||||
their file system path and then running the test. For
|
||||
example if you have unittest2 installed you can type::
|
||||
@@ -82,7 +88,7 @@ You can always peek at the collection tree without running tests like this::
|
||||
|
||||
. $ py.test --collect-only pythoncollection.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 3 items
|
||||
<Module 'pythoncollection.py'>
|
||||
<Function 'test_function'>
|
||||
@@ -98,7 +104,7 @@ customizing test collection to find all .py files
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
You can easily instruct py.test to discover tests from every python file::
|
||||
You can easily instruct ``pytest`` to discover tests from every python file::
|
||||
|
||||
|
||||
# content of pytest.ini
|
||||
@@ -106,8 +112,8 @@ You can easily instruct py.test to discover tests from every python file::
|
||||
python_files = *.py
|
||||
|
||||
However, many projects will have a ``setup.py`` which they don't want to be imported. Moreover, there may files only importable by a specific python version.
|
||||
For such cases you can dynamically define files to be ignored by listing
|
||||
them in a ``conftest.py`` file::
|
||||
For such cases you can dynamically define files to be ignored by listing
|
||||
them in a ``conftest.py`` file::
|
||||
|
||||
# content of conftest.py
|
||||
import sys
|
||||
@@ -130,12 +136,12 @@ and a setup.py dummy file like this::
|
||||
# content of setup.py
|
||||
0/0 # will raise exeption if imported
|
||||
|
||||
then a pytest run on python2 will find the one test when run with a python2
|
||||
then a pytest run on python2 will find the one test when run with a python2
|
||||
interpreters and will leave out the setup.py file::
|
||||
|
||||
|
||||
$ py.test --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
<Module 'pkg/module_py2.py'>
|
||||
<Function 'test_only_on_python2'>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
.. _`tbreportdemo`:
|
||||
|
||||
Demo of Python failure reports with py.test
|
||||
|
||||
Demo of Python failure reports with pytest
|
||||
==================================================
|
||||
|
||||
Here is a nice run of several tens of failures
|
||||
and how py.test presents things (unfortunately
|
||||
and how ``pytest`` presents things (unfortunately
|
||||
not showing the nice colors here in the HTML that you
|
||||
get on the terminal - we are working on that):
|
||||
|
||||
@@ -13,7 +13,7 @@ get on the terminal - we are working on that):
|
||||
|
||||
assertion $ py.test failure_demo.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 39 items
|
||||
|
||||
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
@@ -30,7 +30,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:15: AssertionError
|
||||
_________________________ TestFailing.test_simple __________________________
|
||||
|
||||
self = <failure_demo.TestFailing object at 0x26f8f50>
|
||||
self = <failure_demo.TestFailing object at 0x2afa614fb790>
|
||||
|
||||
def test_simple(self):
|
||||
def f():
|
||||
@@ -40,13 +40,13 @@ get on the terminal - we are working on that):
|
||||
|
||||
> assert f() == g()
|
||||
E assert 42 == 43
|
||||
E + where 42 = <function f at 0x269d5f0>()
|
||||
E + and 43 = <function g at 0x269d6e0>()
|
||||
E + where 42 = <function f at 0x2afa6158a5f0>()
|
||||
E + and 43 = <function g at 0x2afa6158a7d0>()
|
||||
|
||||
failure_demo.py:28: AssertionError
|
||||
____________________ TestFailing.test_simple_multiline _____________________
|
||||
|
||||
self = <failure_demo.TestFailing object at 0x26ade90>
|
||||
self = <failure_demo.TestFailing object at 0x2afa60d16b50>
|
||||
|
||||
def test_simple_multiline(self):
|
||||
otherfunc_multi(
|
||||
@@ -66,19 +66,19 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:11: AssertionError
|
||||
___________________________ TestFailing.test_not ___________________________
|
||||
|
||||
self = <failure_demo.TestFailing object at 0x26aac10>
|
||||
self = <failure_demo.TestFailing object at 0x2afa61560ad0>
|
||||
|
||||
def test_not(self):
|
||||
def f():
|
||||
return 42
|
||||
> assert not f()
|
||||
E assert not 42
|
||||
E + where 42 = <function f at 0x269d8c0>()
|
||||
E + where 42 = <function f at 0x2afa6158a6e0>()
|
||||
|
||||
failure_demo.py:38: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_text _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2861490>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa6154fc90>
|
||||
|
||||
def test_eq_text(self):
|
||||
> assert 'spam' == 'eggs'
|
||||
@@ -89,7 +89,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:42: AssertionError
|
||||
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26ade10>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60da1d10>
|
||||
|
||||
def test_eq_similar_text(self):
|
||||
> assert 'foo 1 bar' == 'foo 2 bar'
|
||||
@@ -102,7 +102,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:45: AssertionError
|
||||
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26f8ad0>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60d45a90>
|
||||
|
||||
def test_eq_multiline_text(self):
|
||||
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
|
||||
@@ -115,7 +115,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:48: AssertionError
|
||||
______________ TestSpecialisedExplanations.test_eq_long_text _______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26aa450>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60d0de50>
|
||||
|
||||
def test_eq_long_text(self):
|
||||
a = '1'*100 + 'a' + '2'*100
|
||||
@@ -132,7 +132,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:53: AssertionError
|
||||
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26ad7d0>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa6154fbd0>
|
||||
|
||||
def test_eq_long_text_multiline(self):
|
||||
a = '1\n'*100 + 'a' + '2\n'*100
|
||||
@@ -156,7 +156,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:58: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_list _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26f8550>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60d16290>
|
||||
|
||||
def test_eq_list(self):
|
||||
> assert [0, 1, 2] == [0, 1, 3]
|
||||
@@ -166,7 +166,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:61: AssertionError
|
||||
______________ TestSpecialisedExplanations.test_eq_list_long _______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26aa310>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60da1c50>
|
||||
|
||||
def test_eq_list_long(self):
|
||||
a = [0]*100 + [1] + [3]*100
|
||||
@@ -178,7 +178,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:66: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_dict _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26a6950>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60d45d90>
|
||||
|
||||
def test_eq_dict(self):
|
||||
> assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
|
||||
@@ -194,7 +194,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:69: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_set __________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26e4210>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa614fb3d0>
|
||||
|
||||
def test_eq_set(self):
|
||||
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
|
||||
@@ -210,7 +210,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:72: AssertionError
|
||||
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26f9c10>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa61560bd0>
|
||||
|
||||
def test_eq_longer_list(self):
|
||||
> assert [1,2] == [1,2,3]
|
||||
@@ -220,7 +220,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:75: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_in_list _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26aac50>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa6154fc10>
|
||||
|
||||
def test_in_list(self):
|
||||
> assert 1 in [0, 2, 3, 4, 5]
|
||||
@@ -229,7 +229,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:78: AssertionError
|
||||
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26a6b90>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60d0db50>
|
||||
|
||||
def test_not_in_text_multiline(self):
|
||||
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
|
||||
@@ -247,7 +247,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:82: AssertionError
|
||||
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26f9d90>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa61548810>
|
||||
|
||||
def test_not_in_text_single(self):
|
||||
text = 'single foo line'
|
||||
@@ -260,7 +260,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:86: AssertionError
|
||||
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26f89d0>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa614f9fd0>
|
||||
|
||||
def test_not_in_text_single_long(self):
|
||||
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
|
||||
@@ -273,7 +273,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:90: AssertionError
|
||||
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x26ad310>
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0x2afa60da1d50>
|
||||
|
||||
def test_not_in_text_single_long_term(self):
|
||||
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
|
||||
@@ -292,7 +292,7 @@ get on the terminal - we are working on that):
|
||||
i = Foo()
|
||||
> assert i.b == 2
|
||||
E assert 1 == 2
|
||||
E + where 1 = <failure_demo.Foo object at 0x26e4650>.b
|
||||
E + where 1 = <failure_demo.Foo object at 0x2afa61548510>.b
|
||||
|
||||
failure_demo.py:101: AssertionError
|
||||
_________________________ test_attribute_instance __________________________
|
||||
@@ -302,8 +302,8 @@ get on the terminal - we are working on that):
|
||||
b = 1
|
||||
> assert Foo().b == 2
|
||||
E assert 1 == 2
|
||||
E + where 1 = <failure_demo.Foo object at 0x26f8c50>.b
|
||||
E + where <failure_demo.Foo object at 0x26f8c50> = <class 'failure_demo.Foo'>()
|
||||
E + where 1 = <failure_demo.Foo object at 0x2afa60d16610>.b
|
||||
E + where <failure_demo.Foo object at 0x2afa60d16610> = <class 'failure_demo.Foo'>()
|
||||
|
||||
failure_demo.py:107: AssertionError
|
||||
__________________________ test_attribute_failure __________________________
|
||||
@@ -319,7 +319,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:116:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
self = <failure_demo.Foo object at 0x26a65d0>
|
||||
self = <failure_demo.Foo object at 0x2afa614fb1d0>
|
||||
|
||||
def _get_b(self):
|
||||
> raise Exception('Failed to get attrib')
|
||||
@@ -335,15 +335,15 @@ get on the terminal - we are working on that):
|
||||
b = 2
|
||||
> assert Foo().b == Bar().b
|
||||
E assert 1 == 2
|
||||
E + where 1 = <failure_demo.Foo object at 0x26ad050>.b
|
||||
E + where <failure_demo.Foo object at 0x26ad050> = <class 'failure_demo.Foo'>()
|
||||
E + and 2 = <failure_demo.Bar object at 0x26ad850>.b
|
||||
E + where <failure_demo.Bar object at 0x26ad850> = <class 'failure_demo.Bar'>()
|
||||
E + where 1 = <failure_demo.Foo object at 0x2afa60da1f50>.b
|
||||
E + where <failure_demo.Foo object at 0x2afa60da1f50> = <class 'failure_demo.Foo'>()
|
||||
E + and 2 = <failure_demo.Bar object at 0x2afa61505c50>.b
|
||||
E + where <failure_demo.Bar object at 0x2afa61505c50> = <class 'failure_demo.Bar'>()
|
||||
|
||||
failure_demo.py:124: AssertionError
|
||||
__________________________ TestRaises.test_raises __________________________
|
||||
|
||||
self = <failure_demo.TestRaises instance at 0x2859e18>
|
||||
self = <failure_demo.TestRaises instance at 0x2afa60d78440>
|
||||
|
||||
def test_raises(self):
|
||||
s = 'qwe'
|
||||
@@ -355,10 +355,10 @@ get on the terminal - we are working on that):
|
||||
> int(s)
|
||||
E ValueError: invalid literal for int() with base 10: 'qwe'
|
||||
|
||||
<0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:905>:1: ValueError
|
||||
<0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:1028>:1: ValueError
|
||||
______________________ TestRaises.test_raises_doesnt _______________________
|
||||
|
||||
self = <failure_demo.TestRaises instance at 0x27013b0>
|
||||
self = <failure_demo.TestRaises instance at 0x2afa6153a7a0>
|
||||
|
||||
def test_raises_doesnt(self):
|
||||
> raises(IOError, "int('3')")
|
||||
@@ -367,7 +367,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:136: Failed
|
||||
__________________________ TestRaises.test_raise ___________________________
|
||||
|
||||
self = <failure_demo.TestRaises instance at 0x271d9e0>
|
||||
self = <failure_demo.TestRaises instance at 0x2afa61542128>
|
||||
|
||||
def test_raise(self):
|
||||
> raise ValueError("demo error")
|
||||
@@ -376,7 +376,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:139: ValueError
|
||||
________________________ TestRaises.test_tupleerror ________________________
|
||||
|
||||
self = <failure_demo.TestRaises instance at 0x270b3f8>
|
||||
self = <failure_demo.TestRaises instance at 0x2afa60dc9e60>
|
||||
|
||||
def test_tupleerror(self):
|
||||
> a,b = [1]
|
||||
@@ -385,7 +385,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:142: ValueError
|
||||
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
|
||||
|
||||
self = <failure_demo.TestRaises instance at 0x26ab368>
|
||||
self = <failure_demo.TestRaises instance at 0x2afa60d69b90>
|
||||
|
||||
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
|
||||
l = [1,2,3]
|
||||
@@ -394,11 +394,11 @@ get on the terminal - we are working on that):
|
||||
E TypeError: 'int' object is not iterable
|
||||
|
||||
failure_demo.py:147: TypeError
|
||||
----------------------------- Captured stdout ------------------------------
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
l is [1, 2, 3]
|
||||
________________________ TestRaises.test_some_error ________________________
|
||||
|
||||
self = <failure_demo.TestRaises instance at 0x271b488>
|
||||
self = <failure_demo.TestRaises instance at 0x2afa60d5c680>
|
||||
|
||||
def test_some_error(self):
|
||||
> if namenotexi:
|
||||
@@ -426,7 +426,7 @@ get on the terminal - we are working on that):
|
||||
<2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError
|
||||
____________________ TestMoreErrors.test_complex_error _____________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x271da28>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa60d6b1b8>
|
||||
|
||||
def test_complex_error(self):
|
||||
def f():
|
||||
@@ -437,13 +437,8 @@ get on the terminal - we are working on that):
|
||||
|
||||
failure_demo.py:175:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
x = 44, y = 43
|
||||
|
||||
def somefunc(x,y):
|
||||
> otherfunc(x,y)
|
||||
|
||||
failure_demo.py:8:
|
||||
failure_demo.py:8: in somefunc
|
||||
otherfunc(x,y)
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
a = 44, b = 43
|
||||
@@ -455,7 +450,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:5: AssertionError
|
||||
___________________ TestMoreErrors.test_z1_unpack_error ____________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2716950>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa61546ef0>
|
||||
|
||||
def test_z1_unpack_error(self):
|
||||
l = []
|
||||
@@ -465,7 +460,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:179: ValueError
|
||||
____________________ TestMoreErrors.test_z2_type_error _____________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x26f5e18>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa60d5e680>
|
||||
|
||||
def test_z2_type_error(self):
|
||||
l = 3
|
||||
@@ -475,19 +470,19 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:183: TypeError
|
||||
______________________ TestMoreErrors.test_startswith ______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x27075f0>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa60d697e8>
|
||||
|
||||
def test_startswith(self):
|
||||
s = "123"
|
||||
g = "456"
|
||||
> assert s.startswith(g)
|
||||
E assert <built-in method startswith of str object at 0x26ff8c8>('456')
|
||||
E + where <built-in method startswith of str object at 0x26ff8c8> = '123'.startswith
|
||||
E assert <built-in method startswith of str object at 0x2afa61549a08>('456')
|
||||
E + where <built-in method startswith of str object at 0x2afa61549a08> = '123'.startswith
|
||||
|
||||
failure_demo.py:188: AssertionError
|
||||
__________________ TestMoreErrors.test_startswith_nested ___________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2707ef0>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa60d4dfc8>
|
||||
|
||||
def test_startswith_nested(self):
|
||||
def f():
|
||||
@@ -495,15 +490,15 @@ get on the terminal - we are working on that):
|
||||
def g():
|
||||
return "456"
|
||||
> assert f().startswith(g())
|
||||
E assert <built-in method startswith of str object at 0x26ff8c8>('456')
|
||||
E + where <built-in method startswith of str object at 0x26ff8c8> = '123'.startswith
|
||||
E + where '123' = <function f at 0x269d7d0>()
|
||||
E + and '456' = <function g at 0x2698ed8>()
|
||||
E assert <built-in method startswith of str object at 0x2afa61549a08>('456')
|
||||
E + where <built-in method startswith of str object at 0x2afa61549a08> = '123'.startswith
|
||||
E + where '123' = <function f at 0x2afa60d37b90>()
|
||||
E + and '456' = <function g at 0x2afa60d37e60>()
|
||||
|
||||
failure_demo.py:195: AssertionError
|
||||
_____________________ TestMoreErrors.test_global_func ______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x271bef0>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa60d4ecf8>
|
||||
|
||||
def test_global_func(self):
|
||||
> assert isinstance(globf(42), float)
|
||||
@@ -513,18 +508,18 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:198: AssertionError
|
||||
_______________________ TestMoreErrors.test_instance _______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x271bb90>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa614fea28>
|
||||
|
||||
def test_instance(self):
|
||||
self.x = 6*7
|
||||
> assert self.x != 42
|
||||
E assert 42 != 42
|
||||
E + where 42 = <failure_demo.TestMoreErrors instance at 0x271bb90>.x
|
||||
E + where 42 = <failure_demo.TestMoreErrors instance at 0x2afa614fea28>.x
|
||||
|
||||
failure_demo.py:202: AssertionError
|
||||
_______________________ TestMoreErrors.test_compare ________________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2634170>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa614fe0e0>
|
||||
|
||||
def test_compare(self):
|
||||
> assert globf(10) < 5
|
||||
@@ -534,7 +529,7 @@ get on the terminal - we are working on that):
|
||||
failure_demo.py:205: AssertionError
|
||||
_____________________ TestMoreErrors.test_try_finally ______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2717f80>
|
||||
self = <failure_demo.TestMoreErrors instance at 0x2afa60d6b830>
|
||||
|
||||
def test_try_finally(self):
|
||||
x = 1
|
||||
@@ -543,4 +538,4 @@ get on the terminal - we are working on that):
|
||||
E assert 1 == 0
|
||||
|
||||
failure_demo.py:210: AssertionError
|
||||
======================== 39 failed in 0.26 seconds =========================
|
||||
======================== 39 failed in 0.21 seconds =========================
|
||||
|
||||
@@ -53,7 +53,7 @@ Let's run this without supplying our new option::
|
||||
E assert 0
|
||||
|
||||
test_sample.py:6: AssertionError
|
||||
----------------------------- Captured stdout ------------------------------
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
first
|
||||
1 failed in 0.01 seconds
|
||||
|
||||
@@ -75,7 +75,7 @@ And now with supplying a command line option::
|
||||
E assert 0
|
||||
|
||||
test_sample.py:6: AssertionError
|
||||
----------------------------- Captured stdout ------------------------------
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
second
|
||||
1 failed in 0.01 seconds
|
||||
|
||||
@@ -108,7 +108,7 @@ directory with the above conftest.py::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 0 items
|
||||
|
||||
============================= in 0.00 seconds =============================
|
||||
@@ -152,12 +152,12 @@ and when running it will see a skipped "slow" test::
|
||||
|
||||
$ py.test -rs # "-rs" means report details on the little 's'
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_module.py .s
|
||||
========================= short test summary info ==========================
|
||||
SKIP [1] /tmp/doc-exec-603/conftest.py:9: need --runslow option to run
|
||||
SKIP [1] /tmp/doc-exec-147/conftest.py:9: need --runslow option to run
|
||||
|
||||
=================== 1 passed, 1 skipped in 0.01 seconds ====================
|
||||
|
||||
@@ -165,7 +165,7 @@ Or run it including the ``slow`` marked test::
|
||||
|
||||
$ py.test --runslow
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_module.py ..
|
||||
@@ -193,7 +193,7 @@ Example::
|
||||
def test_something():
|
||||
checkconfig(42)
|
||||
|
||||
The ``__tracebackhide__`` setting influences py.test showing
|
||||
The ``__tracebackhide__`` setting influences ``pytest`` showing
|
||||
of tracebacks: the ``checkconfig`` function will not be shown
|
||||
unless the ``--fulltrace`` command line option is specified.
|
||||
Let's run our little function::
|
||||
@@ -202,15 +202,15 @@ Let's run our little function::
|
||||
F
|
||||
================================= FAILURES =================================
|
||||
______________________________ test_something ______________________________
|
||||
|
||||
|
||||
def test_something():
|
||||
> checkconfig(42)
|
||||
E Failed: not configured: 42
|
||||
|
||||
|
||||
test_checkconfig.py:8: Failed
|
||||
1 failed in 0.01 seconds
|
||||
|
||||
Detect if running from within a py.test run
|
||||
Detect if running from within a pytest run
|
||||
--------------------------------------------------------------
|
||||
|
||||
.. regendoc:wipe
|
||||
@@ -245,10 +245,10 @@ Adding info to test report header
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
It's easy to present extra information in a py.test run::
|
||||
It's easy to present extra information in a ``pytest`` run::
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
|
||||
def pytest_report_header(config):
|
||||
return "project deps: mylib-1.1"
|
||||
|
||||
@@ -256,7 +256,7 @@ which will add the string to the test header accordingly::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
project deps: mylib-1.1
|
||||
collected 0 items
|
||||
|
||||
@@ -279,7 +279,7 @@ which will add info only when run with "--v"::
|
||||
|
||||
$ py.test -v
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
info1: did you know that ...
|
||||
did you?
|
||||
collecting ... collected 0 items
|
||||
@@ -290,7 +290,7 @@ and nothing when run plainly::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 0 items
|
||||
|
||||
============================= in 0.00 seconds =============================
|
||||
@@ -322,7 +322,7 @@ Now we can profile which test functions execute the slowest::
|
||||
|
||||
$ py.test --durations=3
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 3 items
|
||||
|
||||
test_some_are_slow.py ...
|
||||
@@ -330,7 +330,7 @@ Now we can profile which test functions execute the slowest::
|
||||
========================= slowest 3 test durations =========================
|
||||
0.20s call test_some_are_slow.py::test_funcslow2
|
||||
0.10s call test_some_are_slow.py::test_funcslow1
|
||||
0.00s setup test_some_are_slow.py::test_funcfast
|
||||
0.00s setup test_some_are_slow.py::test_funcslow2
|
||||
========================= 3 passed in 0.31 seconds =========================
|
||||
|
||||
incremental testing - test steps
|
||||
@@ -383,7 +383,7 @@ If we run this::
|
||||
|
||||
$ py.test -rx
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 4 items
|
||||
|
||||
test_step.py .Fx.
|
||||
@@ -391,7 +391,7 @@ If we run this::
|
||||
================================= FAILURES =================================
|
||||
____________________ TestUserHandling.test_modification ____________________
|
||||
|
||||
self = <test_step.TestUserHandling instance at 0x1c6fb90>
|
||||
self = <test_step.TestUserHandling instance at 0x2aca13f66e18>
|
||||
|
||||
def test_modification(self):
|
||||
> assert 0
|
||||
@@ -401,7 +401,7 @@ If we run this::
|
||||
========================= short test summary info ==========================
|
||||
XFAIL test_step.py::TestUserHandling::()::test_deletion
|
||||
reason: previous test failed (test_modification)
|
||||
============== 1 failed, 2 passed, 1 xfailed in 0.02 seconds ===============
|
||||
============== 1 failed, 2 passed, 1 xfailed in 0.01 seconds ===============
|
||||
|
||||
We'll see that ``test_deletion`` was not executed because ``test_modification``
|
||||
failed. It is reported as an "expected failure".
|
||||
@@ -448,12 +448,12 @@ the ``db`` fixture::
|
||||
# content of b/test_error.py
|
||||
def test_root(db): # no db here, will error out
|
||||
pass
|
||||
|
||||
|
||||
We can run this::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 7 items
|
||||
|
||||
test_step.py .Fx.
|
||||
@@ -463,17 +463,17 @@ We can run this::
|
||||
|
||||
================================== ERRORS ==================================
|
||||
_______________________ ERROR at setup of test_root ________________________
|
||||
file /tmp/doc-exec-603/b/test_error.py, line 1
|
||||
file /tmp/doc-exec-147/b/test_error.py, line 1
|
||||
def test_root(db): # no db here, will error out
|
||||
fixture 'db' not found
|
||||
available fixtures: pytestconfig, recwarn, monkeypatch, capfd, capsys, tmpdir
|
||||
available fixtures: tmpdir, monkeypatch, pytestconfig, recwarn, capsys, capfd
|
||||
use 'py.test --fixtures [testpath]' for help on them.
|
||||
|
||||
/tmp/doc-exec-603/b/test_error.py:1
|
||||
/tmp/doc-exec-147/b/test_error.py:1
|
||||
================================= FAILURES =================================
|
||||
____________________ TestUserHandling.test_modification ____________________
|
||||
|
||||
self = <test_step.TestUserHandling instance at 0x22f3518>
|
||||
self = <test_step.TestUserHandling instance at 0x2afc14d78e18>
|
||||
|
||||
def test_modification(self):
|
||||
> assert 0
|
||||
@@ -482,20 +482,20 @@ We can run this::
|
||||
test_step.py:9: AssertionError
|
||||
_________________________________ test_a1 __________________________________
|
||||
|
||||
db = <conftest.DB instance at 0x2304248>
|
||||
db = <conftest.DB instance at 0x2afc145495a8>
|
||||
|
||||
def test_a1(db):
|
||||
> assert 0, db # to show value
|
||||
E AssertionError: <conftest.DB instance at 0x2304248>
|
||||
E AssertionError: <conftest.DB instance at 0x2afc145495a8>
|
||||
|
||||
a/test_db.py:2: AssertionError
|
||||
_________________________________ test_a2 __________________________________
|
||||
|
||||
db = <conftest.DB instance at 0x2304248>
|
||||
db = <conftest.DB instance at 0x2afc145495a8>
|
||||
|
||||
def test_a2(db):
|
||||
> assert 0, db # to show value
|
||||
E AssertionError: <conftest.DB instance at 0x2304248>
|
||||
E AssertionError: <conftest.DB instance at 0x2afc145495a8>
|
||||
|
||||
a/test_db2.py:2: AssertionError
|
||||
========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.03 seconds ==========
|
||||
@@ -512,7 +512,7 @@ post-process test reports / failures
|
||||
---------------------------------------
|
||||
|
||||
If you want to postprocess test reports and need access to the executing
|
||||
environment you can implement a hook that gets called when the test
|
||||
environment you can implement a hook that gets called when the test
|
||||
"report" object is about to be created. Here we write out all failing
|
||||
test calls and also access a fixture (if it was used by the test) in
|
||||
case you want to query/look at it during your post processing. In our
|
||||
@@ -529,7 +529,7 @@ case we just write some informations out to a ``failures`` file::
|
||||
rep = __multicall__.execute()
|
||||
|
||||
# we only look at actual failing test calls, not setup/teardown
|
||||
if rep.when == "call" and rep.failed:
|
||||
if rep.when == "call" and rep.failed:
|
||||
mode = "a" if os.path.exists("failures") else "w"
|
||||
with open("failures", mode) as f:
|
||||
# let's also access a fixture for the fun of it
|
||||
@@ -537,7 +537,7 @@ case we just write some informations out to a ``failures`` file::
|
||||
extra = " (%s)" % item.funcargs["tmpdir"]
|
||||
else:
|
||||
extra = ""
|
||||
|
||||
|
||||
f.write(rep.nodeid + extra + "\n")
|
||||
return rep
|
||||
|
||||
@@ -545,15 +545,15 @@ if you then have failing tests::
|
||||
|
||||
# content of test_module.py
|
||||
def test_fail1(tmpdir):
|
||||
assert 0
|
||||
assert 0
|
||||
def test_fail2():
|
||||
assert 0
|
||||
|
||||
assert 0
|
||||
|
||||
and run them::
|
||||
|
||||
$ py.test test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_module.py FF
|
||||
@@ -561,7 +561,7 @@ and run them::
|
||||
================================= FAILURES =================================
|
||||
________________________________ test_fail1 ________________________________
|
||||
|
||||
tmpdir = local('/tmp/pytest-190/test_fail10')
|
||||
tmpdir = local('/tmp/pytest-28/test_fail10')
|
||||
|
||||
def test_fail1(tmpdir):
|
||||
> assert 0
|
||||
@@ -580,7 +580,7 @@ and run them::
|
||||
you will have a "failures" file which contains the failing test ids::
|
||||
|
||||
$ cat failures
|
||||
test_module.py::test_fail1 (/tmp/pytest-190/test_fail10)
|
||||
test_module.py::test_fail1 (/tmp/pytest-28/test_fail10)
|
||||
test_module.py::test_fail2
|
||||
|
||||
Making test result information available in fixtures
|
||||
@@ -623,27 +623,27 @@ here is a little example implemented via a local plugin::
|
||||
if you then have failing tests::
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def other():
|
||||
assert 0
|
||||
|
||||
|
||||
def test_setup_fails(something, other):
|
||||
pass
|
||||
|
||||
def test_call_fails(something):
|
||||
assert 0
|
||||
assert 0
|
||||
|
||||
def test_fail2():
|
||||
assert 0
|
||||
|
||||
assert 0
|
||||
|
||||
and run it::
|
||||
|
||||
$ py.test -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 3 items
|
||||
|
||||
test_module.py Esetting up a test failed! test_module.py::test_setup_fails
|
||||
@@ -676,7 +676,7 @@ and run it::
|
||||
E assert 0
|
||||
|
||||
test_module.py:15: AssertionError
|
||||
==================== 2 failed, 1 error in 0.02 seconds =====================
|
||||
==================== 2 failed, 1 error in 0.01 seconds =====================
|
||||
|
||||
You'll see that the fixture finalizers could use the precise reporting
|
||||
information.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
A sesssion-fixture which can look at all collected tests
|
||||
A session-fixture which can look at all collected tests
|
||||
----------------------------------------------------------------
|
||||
|
||||
A session-scoped fixture effectively has access to all
|
||||
@@ -70,4 +69,4 @@ If you run this without output capturing::
|
||||
.test other
|
||||
.test_unit1 method called
|
||||
.
|
||||
4 passed in 0.02 seconds
|
||||
4 passed in 0.03 seconds
|
||||
|
||||
@@ -4,37 +4,37 @@ Some Issues and Questions
|
||||
.. note::
|
||||
|
||||
This FAQ is here only mostly for historic reasons. Checkout
|
||||
`pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
|
||||
`pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
|
||||
for many questions and answers related to pytest and/or use
|
||||
:ref:`contact channels` to get help.
|
||||
|
||||
On naming, nosetests, licensing and magic
|
||||
------------------------------------------------
|
||||
|
||||
How does py.test relate to nose and unittest?
|
||||
How does pytest relate to nose and unittest?
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
py.test and nose_ share basic philosophy when it comes
|
||||
``pytest`` and nose_ share basic philosophy when it comes
|
||||
to running and writing Python tests. In fact, you can run many tests
|
||||
written for nose with py.test. nose_ was originally created
|
||||
as a clone of ``py.test`` when py.test was in the ``0.8`` release
|
||||
written for nose with ``pytest``. nose_ was originally created
|
||||
as a clone of ``pytest`` when ``pytest`` was in the ``0.8`` release
|
||||
cycle. Note that starting with pytest-2.0 support for running unittest
|
||||
test suites is majorly improved.
|
||||
|
||||
how does py.test relate to twisted's trial?
|
||||
how does pytest relate to twisted's trial?
|
||||
++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Since some time py.test has builtin support for supporting tests
|
||||
Since some time ``pytest`` has builtin support for supporting tests
|
||||
written using trial. It does not itself start a reactor, however,
|
||||
and does not handle Deferreds returned from a test in pytest style.
|
||||
and does not handle Deferreds returned from a test in pytest style.
|
||||
If you are using trial's unittest.TestCase chances are that you can
|
||||
just run your tests even if you return Deferreds. In addition,
|
||||
there also is a dedicated `pytest-twisted
|
||||
<http://pypi.python.org/pypi/pytest-twisted>`_ plugin which allows to
|
||||
return deferreds from pytest-style tests, allowing to use
|
||||
:ref:`fixtures` and other features.
|
||||
|
||||
how does py.test work with Django?
|
||||
|
||||
how does pytest work with Django?
|
||||
++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
In 2012, some work is going into the `pytest-django plugin <http://pypi.python.org/pypi/pytest-django>`_. It substitutes the usage of Django's
|
||||
@@ -44,36 +44,36 @@ are not available from Django directly.
|
||||
.. _features: features.html
|
||||
|
||||
|
||||
What's this "magic" with py.test? (historic notes)
|
||||
What's this "magic" with pytest? (historic notes)
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Around 2007 (version ``0.8``) some people thought that py.test
|
||||
Around 2007 (version ``0.8``) some people thought that ``pytest``
|
||||
was using too much "magic". It had been part of the `pylib`_ which
|
||||
contains a lot of unreleated python library code. Around 2010 there
|
||||
was a major cleanup refactoring, which removed unused or deprecated code
|
||||
was a major cleanup refactoring, which removed unused or deprecated code
|
||||
and resulted in the new ``pytest`` PyPI package which strictly contains
|
||||
only test-related code. This relese also brought a complete pluginification
|
||||
only test-related code. This release also brought a complete pluginification
|
||||
such that the core is around 300 lines of code and everything else is
|
||||
implemented in plugins. Thus ``pytest`` today is a small, universally runnable
|
||||
implemented in plugins. Thus ``pytest`` today is a small, universally runnable
|
||||
and customizable testing framework for Python. Note, however, that
|
||||
``pytest`` uses metaprogramming techniques and reading its source is
|
||||
``pytest`` uses metaprogramming techniques and reading its source is
|
||||
thus likely not something for Python beginners.
|
||||
|
||||
A second "magic" issue was the assert statement debugging feature.
|
||||
Nowadays, py.test explicitely rewrites assert statements in test modules
|
||||
A second "magic" issue was the assert statement debugging feature.
|
||||
Nowadays, ``pytest`` explicitely rewrites assert statements in test modules
|
||||
in order to provide more useful :ref:`assert feedback <assertfeedback>`.
|
||||
This completely avoids previous issues of confusing assertion-reporting.
|
||||
This completely avoids previous issues of confusing assertion-reporting.
|
||||
It also means, that you can use Python's ``-O`` optimization without loosing
|
||||
assertions in test modules.
|
||||
|
||||
py.test contains a second mostly obsolete assert debugging technique,
|
||||
``pytest`` contains a second, mostly obsolete, assert debugging technique,
|
||||
invoked via ``--assert=reinterpret``, activated by default on
|
||||
Python-2.5: When an ``assert`` statement fails, py.test re-interprets
|
||||
Python-2.5: When an ``assert`` statement fails, ``pytest`` re-interprets
|
||||
the expression part to show intermediate values. This technique suffers
|
||||
from a caveat that the rewriting does not: If your expression has side
|
||||
effects (better to avoid them anyway!) the intermediate values may not
|
||||
be the same, confusing the reinterpreter and obfuscating the initial
|
||||
error (this is also explained at the command line if it happens).
|
||||
error (this is also explained at the command line if it happens).
|
||||
|
||||
You can also turn off all assertion interaction using the
|
||||
``--assertmode=off`` option.
|
||||
@@ -85,7 +85,7 @@ You can also turn off all assertion interaction using the
|
||||
Why a ``py.test`` instead of a ``pytest`` command?
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Some of the reasons are historic, others are practical. ``py.test``
|
||||
Some of the reasons are historic, others are practical. ``pytest``
|
||||
used to be part of the ``py`` package which provided several developer
|
||||
utilities, all starting with ``py.<TAB>``, thus providing nice
|
||||
TAB-completion. If
|
||||
@@ -140,16 +140,16 @@ However, with pytest-2.3 you can use the :ref:`@pytest.fixture` decorator
|
||||
and specify ``params`` so that all tests depending on the factory-created
|
||||
resource will run multiple times with different parameters.
|
||||
|
||||
You can also use the `pytest_generate_tests`_ hook to
|
||||
You can also use the `pytest_generate_tests`_ hook to
|
||||
implement the `parametrization scheme of your choice`_.
|
||||
|
||||
.. _`pytest_generate_tests`: test/funcargs.html#parametrizing-tests
|
||||
.. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
|
||||
|
||||
py.test interaction with other packages
|
||||
pytest interaction with other packages
|
||||
---------------------------------------------------
|
||||
|
||||
Issues with py.test, multiprocess and setuptools?
|
||||
Issues with pytest, multiprocess and setuptools?
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
On windows the multiprocess package will instantiate sub processes
|
||||
|
||||
@@ -15,7 +15,7 @@ pytest fixtures: explicit, modular, scalable
|
||||
|
||||
The `purpose of test fixtures`_ is to provide a fixed baseline
|
||||
upon which tests can reliably and repeatedly execute. pytest fixtures
|
||||
offer dramatic improvements over the classic xUnit style of setup/teardown
|
||||
offer dramatic improvements over the classic xUnit style of setup/teardown
|
||||
functions:
|
||||
|
||||
* fixtures have explicit names and are activated by declaring their use
|
||||
@@ -50,7 +50,7 @@ Fixtures as Function arguments
|
||||
-----------------------------------------
|
||||
|
||||
Test functions can receive fixture objects by naming them as an input
|
||||
argument. For each argument name, a fixture function with that name provides
|
||||
argument. For each argument name, a fixture function with that name provides
|
||||
the fixture object. Fixture functions are registered by marking them with
|
||||
:py:func:`@pytest.fixture <_pytest.python.fixture>`. Let's look at a simple
|
||||
self-contained test module containing a fixture and a test function
|
||||
@@ -70,13 +70,13 @@ using it::
|
||||
assert "merlinux" in msg
|
||||
assert 0 # for demo purposes
|
||||
|
||||
Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
|
||||
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
||||
Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
|
||||
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
||||
marked ``smtp`` fixture function. Running the test looks like this::
|
||||
|
||||
$ py.test test_smtpsimple.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
test_smtpsimple.py F
|
||||
@@ -84,7 +84,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
|
||||
================================= FAILURES =================================
|
||||
________________________________ test_ehlo _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x2bb9d88>
|
||||
smtp = <smtplib.SMTP instance at 0x2b8dbdd43638>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response, msg = smtp.ehlo()
|
||||
@@ -94,12 +94,12 @@ marked ``smtp`` fixture function. Running the test looks like this::
|
||||
E assert 0
|
||||
|
||||
test_smtpsimple.py:12: AssertionError
|
||||
========================= 1 failed in 0.18 seconds =========================
|
||||
========================= 1 failed in 0.15 seconds =========================
|
||||
|
||||
In the failure traceback we see that the test function was called with a
|
||||
``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture
|
||||
function. The test function fails on our deliberate ``assert 0``. Here is
|
||||
an exact protocol of how py.test comes to call the test function this way:
|
||||
the exact protocol used by ``pytest`` to call the test function this way:
|
||||
|
||||
1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
|
||||
of the ``test_`` prefix. The test function needs a function argument
|
||||
@@ -125,7 +125,7 @@ with a list of available function arguments.
|
||||
|
||||
In versions prior to 2.3 there was no ``@pytest.fixture`` marker
|
||||
and you had to use a magic ``pytest_funcarg__NAME`` prefix
|
||||
for the fixture factory. This remains and will remain supported
|
||||
for the fixture factory. This remains and will remain supported
|
||||
but is not anymore advertised as the primary means of declaring fixture
|
||||
functions.
|
||||
|
||||
@@ -153,15 +153,15 @@ Sharing a fixture across tests in a module (or class/session)
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
Fixtures requiring network access depend on connectivity and are
|
||||
Fixtures requiring network access depend on connectivity and are
|
||||
usually time-expensive to create. Extending the previous example, we
|
||||
can add a ``scope='module'`` parameter to the
|
||||
can add a ``scope='module'`` parameter to the
|
||||
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
|
||||
to cause the decorated ``smtp`` fixture function to only be invoked once
|
||||
to cause the decorated ``smtp`` fixture function to only be invoked once
|
||||
per test module. Multiple test functions in a test module will thus
|
||||
each receive the same ``smtp`` fixture instance. The next example puts
|
||||
the fixture function into a separate ``conftest.py`` file so
|
||||
that tests from multiple test modules in the directory can
|
||||
that tests from multiple test modules in the directory can
|
||||
access the fixture function::
|
||||
|
||||
# content of conftest.py
|
||||
@@ -180,7 +180,7 @@ function (in or below the directory where ``conftest.py`` is located)::
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
assert response[0] == 250
|
||||
assert "merlinux" in response[1]
|
||||
assert 0 # for demo purposes
|
||||
|
||||
@@ -194,7 +194,7 @@ inspect what is going on and can now run the tests::
|
||||
|
||||
$ py.test test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 2 items
|
||||
|
||||
test_module.py FF
|
||||
@@ -202,7 +202,7 @@ inspect what is going on and can now run the tests::
|
||||
================================= FAILURES =================================
|
||||
________________________________ test_ehlo _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x18f2fc8>
|
||||
smtp = <smtplib.SMTP instance at 0x2b0d30a59f38>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
@@ -214,7 +214,7 @@ inspect what is going on and can now run the tests::
|
||||
test_module.py:6: AssertionError
|
||||
________________________________ test_noop _________________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x18f2fc8>
|
||||
smtp = <smtplib.SMTP instance at 0x2b0d30a59f38>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
@@ -225,26 +225,26 @@ inspect what is going on and can now run the tests::
|
||||
test_module.py:11: AssertionError
|
||||
========================= 2 failed in 0.16 seconds =========================
|
||||
|
||||
You see the two ``assert 0`` failing and more importantly you can also see
|
||||
that the same (module-scoped) ``smtp`` object was passed into the two
|
||||
test functions because pytest shows the incoming argument values in the
|
||||
You see the two ``assert 0`` failing and more importantly you can also see
|
||||
that the same (module-scoped) ``smtp`` object was passed into the two
|
||||
test functions because pytest shows the incoming argument values in the
|
||||
traceback. As a result, the two test functions using ``smtp`` run as
|
||||
quick as a single one because they reuse the same instance.
|
||||
|
||||
If you decide that you rather want to have a session-scoped ``smtp``
|
||||
instance, you can simply declare it::
|
||||
|
||||
@pytest.fixture(scope=``session``)
|
||||
@pytest.fixture(scope="session")
|
||||
def smtp(...):
|
||||
# the returned fixture value will be shared for
|
||||
# the returned fixture value will be shared for
|
||||
# all tests needing it
|
||||
|
||||
.. _`finalization`:
|
||||
|
||||
fixture finalization / executing teardown code
|
||||
|
||||
fixture finalization / executing teardown code
|
||||
-------------------------------------------------------------
|
||||
|
||||
pytest supports execution of fixture specific finalization code
|
||||
pytest supports execution of fixture specific finalization code
|
||||
when the fixture goes out of scope. By accepting a ``request`` object
|
||||
into your fixture function you can call its ``request.addfinalizer`` one
|
||||
or multiple times::
|
||||
@@ -271,12 +271,12 @@ Let's execute it::
|
||||
$ py.test -s -q --tb=no
|
||||
FFteardown smtp
|
||||
|
||||
2 failed in 0.15 seconds
|
||||
2 failed in 0.16 seconds
|
||||
|
||||
We see that the ``smtp`` instance is finalized after the two
|
||||
tests finished execution. Note that if we decorated our fixture
|
||||
function with ``scope='function'`` then fixture setup and cleanup would
|
||||
occur around each single test. In either case the test
|
||||
occur around each single test. In either case the test
|
||||
module itself does not need to change or know about these details
|
||||
of fixture setup.
|
||||
|
||||
@@ -288,7 +288,7 @@ Fixtures can introspect the requesting test context
|
||||
|
||||
Fixture function can accept the :py:class:`request <FixtureRequest>` object
|
||||
to introspect the "requesting" test function, class or module context.
|
||||
Further extending the previous ``smtp`` fixture example, let's
|
||||
Further extending the previous ``smtp`` fixture example, let's
|
||||
read an optional server URL from the test module which uses our fixture::
|
||||
|
||||
# content of conftest.py
|
||||
@@ -299,12 +299,12 @@ read an optional server URL from the test module which uses our fixture::
|
||||
def smtp(request):
|
||||
server = getattr(request.module, "smtpserver", "merlinux.eu")
|
||||
smtp = smtplib.SMTP(server)
|
||||
|
||||
|
||||
def fin():
|
||||
print ("finalizing %s (%s)" % (smtp, server))
|
||||
smtp.close()
|
||||
|
||||
return smtp
|
||||
|
||||
return smtp
|
||||
|
||||
We use the ``request.module`` attribute to optionally obtain an
|
||||
``smtpserver`` attribute from the test module. If we just execute
|
||||
@@ -312,13 +312,13 @@ again, nothing much has changed::
|
||||
|
||||
$ py.test -s -q --tb=no
|
||||
FF
|
||||
2 failed in 0.16 seconds
|
||||
2 failed in 0.17 seconds
|
||||
|
||||
Let's quickly create another test module that actually sets the
|
||||
server URL in its module namespace::
|
||||
|
||||
|
||||
# content of test_anothersmtp.py
|
||||
|
||||
|
||||
smtpserver = "mail.python.org" # will be read by smtp fixture
|
||||
|
||||
def test_showhelo(smtp):
|
||||
@@ -331,8 +331,8 @@ Running it::
|
||||
================================= FAILURES =================================
|
||||
______________________________ test_showhelo _______________________________
|
||||
test_anothersmtp.py:5: in test_showhelo
|
||||
> assert 0, smtp.helo()
|
||||
E AssertionError: (250, 'mail.python.org')
|
||||
assert 0, smtp.helo()
|
||||
E AssertionError: (250, 'mail.python.org')
|
||||
|
||||
voila! The ``smtp`` fixture function picked up our mail server name
|
||||
from the module namespace.
|
||||
@@ -346,7 +346,7 @@ Fixture functions can be parametrized in which case they will be called
|
||||
multiple times, each time executing the set of dependent tests, i. e. the
|
||||
tests that depend on this fixture. Test functions do usually not need
|
||||
to be aware of their re-running. Fixture parametrization helps to
|
||||
write exhaustive functional tests for components which themselves can be
|
||||
write exhaustive functional tests for components which themselves can be
|
||||
configured in multiple ways.
|
||||
|
||||
Extending the previous example, we can flag the fixture to create two
|
||||
@@ -358,7 +358,7 @@ through the special :py:class:`request <FixtureRequest>` object::
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.fixture(scope="module",
|
||||
@pytest.fixture(scope="module",
|
||||
params=["merlinux.eu", "mail.python.org"])
|
||||
def smtp(request):
|
||||
smtp = smtplib.SMTP(request.param)
|
||||
@@ -368,10 +368,10 @@ through the special :py:class:`request <FixtureRequest>` object::
|
||||
request.addfinalizer(fin)
|
||||
return smtp
|
||||
|
||||
The main change is the declaration of ``params`` with
|
||||
The main change is the declaration of ``params`` with
|
||||
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
|
||||
for each of which the fixture function will execute and can access
|
||||
a value via ``request.param``. No test function code needs to change.
|
||||
a value via ``request.param``. No test function code needs to change.
|
||||
So let's just do another run::
|
||||
|
||||
$ py.test -q test_module.py
|
||||
@@ -379,7 +379,7 @@ So let's just do another run::
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_ehlo[merlinux.eu] __________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x2662290>
|
||||
smtp = <smtplib.SMTP instance at 0x2ba3fee43950>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
@@ -391,7 +391,7 @@ So let's just do another run::
|
||||
test_module.py:6: AssertionError
|
||||
__________________________ test_noop[merlinux.eu] __________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x2662290>
|
||||
smtp = <smtplib.SMTP instance at 0x2ba3fee43950>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
@@ -402,18 +402,20 @@ So let's just do another run::
|
||||
test_module.py:11: AssertionError
|
||||
________________________ test_ehlo[mail.python.org] ________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x26c2dd0>
|
||||
smtp = <smtplib.SMTP instance at 0x2ba3fedf9ea8>
|
||||
|
||||
def test_ehlo(smtp):
|
||||
response = smtp.ehlo()
|
||||
assert response[0] == 250
|
||||
> assert "merlinux" in response[1]
|
||||
E assert 'merlinux' in 'mail.python.org\nSIZE 25600000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
|
||||
E assert 'merlinux' in 'mail.python.org\nSIZE 25600000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
|
||||
|
||||
test_module.py:5: AssertionError
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
finalizing <smtplib.SMTP instance at 0x2ba3fee43950>
|
||||
________________________ test_noop[mail.python.org] ________________________
|
||||
|
||||
smtp = <smtplib.SMTP instance at 0x26c2dd0>
|
||||
smtp = <smtplib.SMTP instance at 0x2ba3fedf9ea8>
|
||||
|
||||
def test_noop(smtp):
|
||||
response = smtp.noop()
|
||||
@@ -422,11 +424,11 @@ So let's just do another run::
|
||||
E assert 0
|
||||
|
||||
test_module.py:11: AssertionError
|
||||
4 failed in 6.32 seconds
|
||||
4 failed in 5.62 seconds
|
||||
|
||||
We see that our two test functions each ran twice, against the different
|
||||
``smtp`` instances. Note also, that with the ``mail.python.org``
|
||||
connection the second test fails in ``test_ehlo`` because a
|
||||
``smtp`` instances. Note also, that with the ``mail.python.org``
|
||||
connection the second test fails in ``test_ehlo`` because a
|
||||
different server string is expected than what arrived.
|
||||
|
||||
|
||||
@@ -443,7 +445,7 @@ and instantiate an object ``app`` where we stick the already defined
|
||||
``smtp`` resource into it::
|
||||
|
||||
# content of test_appsetup.py
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
class App:
|
||||
@@ -462,22 +464,22 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||
|
||||
$ py.test -v test_appsetup.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
|
||||
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
|
||||
test_appsetup.py@12::test_smtp_exists[merlinux.eu] PASSED
|
||||
test_appsetup.py@12::test_smtp_exists[mail.python.org] PASSED
|
||||
|
||||
========================= 2 passed in 5.75 seconds =========================
|
||||
========================= 2 passed in 6.27 seconds =========================
|
||||
|
||||
Due to the parametrization of ``smtp`` the test will run twice with two
|
||||
different ``App`` instances and respective smtp servers. There is no
|
||||
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
|
||||
as pytest will fully analyse the fixture dependency graph.
|
||||
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
|
||||
as pytest will fully analyse the fixture dependency graph.
|
||||
|
||||
Note, that the ``app`` fixture has a scope of ``module`` and uses a
|
||||
module-scoped ``smtp`` fixture. The example would still work if ``smtp``
|
||||
was cached on a ``session`` scope: it is fine for fixtures to use
|
||||
was cached on a ``session`` scope: it is fine for fixtures to use
|
||||
"broader" scoped fixtures but not the other way round:
|
||||
A session-scoped fixture could not use a module-scoped one in a
|
||||
meaningful way.
|
||||
@@ -492,11 +494,11 @@ Automatic grouping of tests by fixture instances
|
||||
|
||||
pytest minimizes the number of active fixtures during test runs.
|
||||
If you have a parametrized fixture, then all the tests using it will
|
||||
first execute with one instance and then finalizers are called
|
||||
first execute with one instance and then finalizers are called
|
||||
before the next fixture instance is created. Among other things,
|
||||
this eases testing of applications which create and use global state.
|
||||
|
||||
The following example uses two parametrized funcargs, one of which is
|
||||
The following example uses two parametrized funcargs, one of which is
|
||||
scoped on a per-module basis, and all the functions perform ``print`` calls
|
||||
to show the setup/teardown flow::
|
||||
|
||||
@@ -526,32 +528,32 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||
|
||||
$ py.test -v -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0 -- /home/hpk/p/pytest/.tox/regen/bin/python
|
||||
collecting ... collected 8 items
|
||||
|
||||
test_module.py:15: test_0[1] test0 1
|
||||
test_module.py@15::test_0[1] test0 1
|
||||
PASSED
|
||||
test_module.py:15: test_0[2] test0 2
|
||||
test_module.py@15::test_0[2] test0 2
|
||||
PASSED
|
||||
test_module.py:17: test_1[mod1] create mod1
|
||||
test_module.py@17::test_1[mod1] create mod1
|
||||
test1 mod1
|
||||
PASSED
|
||||
test_module.py:19: test_2[1-mod1] test2 1 mod1
|
||||
test_module.py@19::test_2[1-mod1] test2 1 mod1
|
||||
PASSED
|
||||
test_module.py:19: test_2[2-mod1] test2 2 mod1
|
||||
test_module.py@19::test_2[2-mod1] test2 2 mod1
|
||||
PASSED
|
||||
test_module.py:17: test_1[mod2] create mod2
|
||||
test_module.py@17::test_1[mod2] create mod2
|
||||
test1 mod2
|
||||
PASSED
|
||||
test_module.py:19: test_2[1-mod2] test2 1 mod2
|
||||
test_module.py@19::test_2[1-mod2] test2 1 mod2
|
||||
PASSED
|
||||
test_module.py:19: test_2[2-mod2] test2 2 mod2
|
||||
test_module.py@19::test_2[2-mod2] test2 2 mod2
|
||||
PASSED
|
||||
|
||||
========================= 8 passed in 0.01 seconds =========================
|
||||
|
||||
You can see that the parametrized module-scoped ``modarg`` resource caused
|
||||
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
||||
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
||||
before the ``mod2`` resource was setup.
|
||||
|
||||
|
||||
@@ -571,7 +573,7 @@ achieve it. We separate the creation of the fixture into a conftest.py
|
||||
file::
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
@@ -610,12 +612,12 @@ You can specify multiple fixtures like this::
|
||||
|
||||
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
||||
|
||||
and you may specify fixture usage at the test module level, using
|
||||
and you may specify fixture usage at the test module level, using
|
||||
a generic feature of the mark mechanism::
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("cleandir")
|
||||
|
||||
Lastly you can put fixtures required by all tests in your project
|
||||
Lastly you can put fixtures required by all tests in your project
|
||||
into an ini-file::
|
||||
|
||||
# content of pytest.ini
|
||||
@@ -624,6 +626,7 @@ into an ini-file::
|
||||
usefixtures = cleandir
|
||||
|
||||
|
||||
.. _`autouse`:
|
||||
.. _`autouse fixtures`:
|
||||
|
||||
autouse fixtures (xUnit setup on steroids)
|
||||
@@ -632,14 +635,14 @@ autouse fixtures (xUnit setup on steroids)
|
||||
.. regendoc:wipe
|
||||
|
||||
Occasionally, you may want to have fixtures get invoked automatically
|
||||
without a `usefixtures`_ or `funcargs`_ reference. As a practical
|
||||
without a `usefixtures`_ or `funcargs`_ reference. As a practical
|
||||
example, suppose we have a database fixture which has a
|
||||
begin/rollback/commit architecture and we want to automatically surround
|
||||
each test method by a transaction and a rollback. Here is a dummy
|
||||
self-contained implementation of this idea::
|
||||
|
||||
# content of test_db_transact.py
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
class DB:
|
||||
@@ -679,11 +682,11 @@ If we run it, we get two passing tests::
|
||||
|
||||
Here is how autouse fixtures work in other scopes:
|
||||
|
||||
- if an autouse fixture is defined in a test module, all its test
|
||||
functions automatically use it.
|
||||
- if an autouse fixture is defined in a test module, all its test
|
||||
functions automatically use it.
|
||||
|
||||
- if an autouse fixture is defined in a conftest.py file then all tests in
|
||||
all test modules belows its directory will invoke the fixture.
|
||||
- if an autouse fixture is defined in a conftest.py file then all tests in
|
||||
all test modules belows its directory will invoke the fixture.
|
||||
|
||||
- lastly, and **please use that with care**: if you define an autouse
|
||||
fixture in a plugin, it will be invoked for all tests in all projects
|
||||
@@ -694,7 +697,7 @@ Here is how autouse fixtures work in other scopes:
|
||||
|
||||
Note that the above ``transact`` fixture may very well be a fixture that
|
||||
you want to make available in your project without having it generally
|
||||
active. The canonical way to do that is to put the transact definition
|
||||
active. The canonical way to do that is to put the transact definition
|
||||
into a conftest.py file **without** using ``autouse``::
|
||||
|
||||
# content of conftest.py
|
||||
@@ -711,7 +714,7 @@ and then e.g. have a TestClass using it by declaring the need::
|
||||
...
|
||||
|
||||
All test methods in this TestClass will use the transaction fixture while
|
||||
other test classes or functions in the module will not use it unless
|
||||
other test classes or functions in the module will not use it unless
|
||||
they also add a ``transact`` reference.
|
||||
|
||||
Shifting (visibility of) fixture functions
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
Installation and Getting Started
|
||||
===================================
|
||||
|
||||
**Pythons**: Python 2.4-3.3, Jython, PyPy
|
||||
**Pythons**: Python 2.6-3.4, Jython, PyPy-2.3
|
||||
|
||||
**Platforms**: Unix/Posix and Windows
|
||||
|
||||
**PyPI package name**: `pytest <http://pypi.python.org/pypi/pytest>`_
|
||||
|
||||
**dependencies**: `py <http://pypi.python.org/pypi/py>`_,
|
||||
`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
|
||||
`argparse (py26) <http://pypi.python.org/pypi/argparse>`_.
|
||||
|
||||
**documentation as PDF**: `download latest <http://pytest.org/latest/pytest.pdf>`_
|
||||
|
||||
.. _`getstarted`:
|
||||
@@ -23,7 +27,7 @@ Installation options::
|
||||
To check your installation has installed the correct version::
|
||||
|
||||
$ py.test --version
|
||||
This is py.test version 2.4.2, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc
|
||||
This is pytest version 2.6.0, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc
|
||||
|
||||
If you get an error checkout :ref:`installation issues`.
|
||||
|
||||
@@ -45,7 +49,7 @@ That's it. You can execute the test function now::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 1 items
|
||||
|
||||
test_sample.py F
|
||||
@@ -61,7 +65,7 @@ That's it. You can execute the test function now::
|
||||
test_sample.py:5: AssertionError
|
||||
========================= 1 failed in 0.01 seconds =========================
|
||||
|
||||
py.test found the ``test_answer`` function by following :ref:`standard test discovery rules <test discovery>`, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``.
|
||||
``pytest`` found the ``test_answer`` function by following :ref:`standard test discovery rules <test discovery>`, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -93,7 +97,7 @@ Running it with, this time in "quiet" reporting mode::
|
||||
|
||||
$ py.test -q test_sysexit.py
|
||||
.
|
||||
1 passed in 0.01 seconds
|
||||
1 passed in 0.00 seconds
|
||||
|
||||
.. todo:: For further ways to assert exceptions see the `raises`
|
||||
|
||||
@@ -123,7 +127,7 @@ run the module by passing its filename::
|
||||
================================= FAILURES =================================
|
||||
____________________________ TestClass.test_two ____________________________
|
||||
|
||||
self = <test_class.TestClass instance at 0x1e1f518>
|
||||
self = <test_class.TestClass instance at 0x2b0b0ac73098>
|
||||
|
||||
def test_two(self):
|
||||
x = "hello"
|
||||
@@ -142,7 +146,7 @@ Going functional: requesting a unique temporary directory
|
||||
|
||||
For functional tests one often needs to create some files
|
||||
and pass them to application objects. pytest provides
|
||||
:ref:`builtinfixtures` which allow to request arbitrary
|
||||
:ref:`builtinfixtures` which allow to request arbitrary
|
||||
resources, for example a unique temporary directory::
|
||||
|
||||
# content of test_tmpdir.py
|
||||
@@ -151,7 +155,7 @@ resources, for example a unique temporary directory::
|
||||
assert 0
|
||||
|
||||
We list the name ``tmpdir`` in the test function signature and
|
||||
py.test will lookup and call a fixture factory to create the resource
|
||||
``pytest`` will lookup and call a fixture factory to create the resource
|
||||
before performing the test function call. Let's just run it::
|
||||
|
||||
$ py.test -q test_tmpdir.py
|
||||
@@ -159,7 +163,7 @@ before performing the test function call. Let's just run it::
|
||||
================================= FAILURES =================================
|
||||
_____________________________ test_needsfiles ______________________________
|
||||
|
||||
tmpdir = local('/tmp/pytest-186/test_needsfiles0')
|
||||
tmpdir = local('/tmp/pytest-24/test_needsfiles0')
|
||||
|
||||
def test_needsfiles(tmpdir):
|
||||
print tmpdir
|
||||
@@ -167,8 +171,8 @@ before performing the test function call. Let's just run it::
|
||||
E assert 0
|
||||
|
||||
test_tmpdir.py:3: AssertionError
|
||||
----------------------------- Captured stdout ------------------------------
|
||||
/tmp/pytest-186/test_needsfiles0
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
/tmp/pytest-24/test_needsfiles0
|
||||
1 failed in 0.01 seconds
|
||||
|
||||
Before the test runs, a unique-per-test-invocation temporary directory
|
||||
@@ -186,7 +190,7 @@ Here are a few suggestions where to go next:
|
||||
* :ref:`cmdline` for command line invocation examples
|
||||
* :ref:`good practises <goodpractises>` for virtualenv, test layout, genscript support
|
||||
* :ref:`fixtures` for providing a functional baseline to your tests
|
||||
* :ref:`apiref` for documentation and examples on using py.test
|
||||
* :ref:`apiref` for documentation and examples on using ``pytest``
|
||||
* :ref:`plugins` managing and writing plugins
|
||||
|
||||
.. _`installation issues`:
|
||||
@@ -221,7 +225,7 @@ py.test not found on Windows despite installation?
|
||||
- **Jython2.5.1 on Windows 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 with Jython.
|
||||
``jython mytest`` to run your tests with Jython using ``pytest``.
|
||||
|
||||
:ref:`examples` for more complex examples
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
.. highlightlang:: python
|
||||
.. _`goodpractises`:
|
||||
|
||||
@@ -8,149 +7,51 @@ Good Integration Practises
|
||||
Work with virtual environments
|
||||
-----------------------------------------------------------
|
||||
|
||||
We recommend to use virtualenv_ environments and use easy_install_
|
||||
(or pip_) for installing your application dependencies as well as
|
||||
the ``pytest`` package itself. This way you will get a much more reproducible
|
||||
environment. A good tool to help you automate test runs against multiple
|
||||
dependency configurations or Python interpreters is `tox`_.
|
||||
We recommend to use virtualenv_ environments and use pip_
|
||||
(or easy_install_) for installing your application and any dependencies
|
||||
as well as the ``pytest`` package itself. This way you will get an isolated
|
||||
and reproducible environment. Given you have installed virtualenv_
|
||||
and execute it from the command line, here is an example session for unix
|
||||
or windows::
|
||||
|
||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||
.. _`buildout`: http://www.buildout.org/
|
||||
.. _pip: http://pypi.python.org/pypi/pip
|
||||
virtualenv . # create a virtualenv directory in the current directory
|
||||
|
||||
Use tox and Continuous Integration servers
|
||||
-------------------------------------------------
|
||||
source bin/activate # on unix
|
||||
|
||||
If you frequently release code to the public you
|
||||
may want to look into `tox`_, the virtualenv test automation
|
||||
tool and its `pytest support <http://testrun.org/tox/latest/example/pytest.html>`_.
|
||||
The basic idea is to generate a JUnitXML file through the ``--junitxml=PATH`` option and have a continuous integration server like Jenkins_ pick it up
|
||||
and generate reports.
|
||||
scripts/activate # on Windows
|
||||
|
||||
.. _standalone:
|
||||
.. _`genscript method`:
|
||||
We can now install pytest::
|
||||
|
||||
Create a py.test standalone script
|
||||
-------------------------------------------
|
||||
pip install pytest
|
||||
|
||||
If you are a maintainer or application developer and want others
|
||||
to easily run tests you can generate a completely standalone "py.test"
|
||||
script::
|
||||
|
||||
py.test --genscript=runtests.py
|
||||
|
||||
generates a ``runtests.py`` script which is a fully functional basic
|
||||
``py.test`` script, running unchanged under Python2 and Python3.
|
||||
You can tell people to download the script and then e.g. run it like this::
|
||||
|
||||
python runtests.py
|
||||
|
||||
|
||||
|
||||
|
||||
Integrating with distutils / ``python setup.py test``
|
||||
--------------------------------------------------------
|
||||
|
||||
You can integrate test runs into your distutils or
|
||||
setuptools based project. Use the `genscript method`_
|
||||
to generate a standalone py.test script::
|
||||
|
||||
py.test --genscript=runtests.py
|
||||
|
||||
and make this script part of your distribution and then add
|
||||
this to your ``setup.py`` file::
|
||||
|
||||
from distutils.core import setup, Command
|
||||
# you can also import from setuptools
|
||||
|
||||
class PyTest(Command):
|
||||
user_options = []
|
||||
def initialize_options(self):
|
||||
pass
|
||||
def finalize_options(self):
|
||||
pass
|
||||
def run(self):
|
||||
import sys,subprocess
|
||||
errno = subprocess.call([sys.executable, 'runtests.py'])
|
||||
raise SystemExit(errno)
|
||||
setup(
|
||||
#...,
|
||||
cmdclass = {'test': PyTest},
|
||||
#...,
|
||||
)
|
||||
|
||||
If you now type::
|
||||
|
||||
python setup.py test
|
||||
|
||||
this will execute your tests using ``runtests.py``. As this is a
|
||||
standalone version of ``py.test`` no prior installation whatsoever is
|
||||
required for calling the test command. You can also pass additional
|
||||
arguments to the subprocess-calls such as your test directory or other
|
||||
options.
|
||||
|
||||
|
||||
Integration with setuptools test commands
|
||||
----------------------------------------------------
|
||||
|
||||
Setuptools supports writing our own Test command for invoking
|
||||
pytest::
|
||||
|
||||
from setuptools.command.test import test as TestCommand
|
||||
import sys
|
||||
|
||||
class PyTest(TestCommand):
|
||||
def finalize_options(self):
|
||||
TestCommand.finalize_options(self)
|
||||
self.test_args = []
|
||||
self.test_suite = True
|
||||
def run_tests(self):
|
||||
#import here, cause outside the eggs aren't loaded
|
||||
import pytest
|
||||
errno = pytest.main(self.test_args)
|
||||
sys.exit(errno)
|
||||
|
||||
setup(
|
||||
#...,
|
||||
tests_require=['pytest'],
|
||||
cmdclass = {'test': PyTest},
|
||||
)
|
||||
|
||||
Now if you run::
|
||||
|
||||
python setup.py test
|
||||
|
||||
this will download py.test if needed and then run py.test
|
||||
as you would expect it to.
|
||||
|
||||
.. _`test discovery`:
|
||||
.. _`Python test discovery`:
|
||||
|
||||
Conventions for Python test discovery
|
||||
-------------------------------------------------
|
||||
|
||||
``py.test`` implements the following standard test discovery:
|
||||
|
||||
* collection starts from the initial command line arguments
|
||||
which may be directories, filenames or test ids.
|
||||
* recurse into directories, unless they match :confval:`norecursedirs`
|
||||
* ``test_*.py`` or ``*_test.py`` files, imported by their `package name`_.
|
||||
* ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
* ``test_`` prefixed test functions or methods are test items
|
||||
|
||||
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
|
||||
|
||||
Within Python modules, py.test also discovers tests using the standard
|
||||
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
|
||||
Due to the ``activate`` step above the ``pip`` will come from
|
||||
the virtualenv directory and install any package into the isolated
|
||||
virtual environment.
|
||||
|
||||
Choosing a test layout / import rules
|
||||
------------------------------------------
|
||||
|
||||
py.test supports common test layouts:
|
||||
``pytest`` supports two common test layouts:
|
||||
|
||||
* inlining test directories into your application package, useful if you want to
|
||||
keep (unit) tests and actually tested code close together::
|
||||
* putting tests into an extra directory outside your actual application
|
||||
code, useful if you have many functional tests or for other reasons
|
||||
want to keep tests separate from actual application code (often a good
|
||||
idea)::
|
||||
|
||||
setup.py # your distutils/setuptools Python package metadata
|
||||
mypkg/
|
||||
__init__.py
|
||||
appmodule.py
|
||||
tests/
|
||||
test_app.py
|
||||
...
|
||||
|
||||
|
||||
* inlining test directories into your application package, useful if you
|
||||
have direct relation between (unit-)test and application modules and
|
||||
want to distribute your tests along with your application::
|
||||
|
||||
setup.py # your distutils/setuptools Python package metadata
|
||||
mypkg/
|
||||
__init__.py
|
||||
appmodule.py
|
||||
@@ -159,21 +60,25 @@ py.test supports common test layouts:
|
||||
test_app.py
|
||||
...
|
||||
|
||||
* putting tests into an extra directory outside your actual application
|
||||
code, useful if you have many functional tests or want to keep
|
||||
tests separate from actual application code::
|
||||
Important notes relating to both schemes:
|
||||
|
||||
mypkg/
|
||||
__init__.py
|
||||
appmodule.py
|
||||
tests/
|
||||
test_app.py
|
||||
...
|
||||
- **make sure that "mypkg" is importable**, for example by typing once::
|
||||
|
||||
In both cases you usually need to make sure that ``mypkg`` is importable,
|
||||
for example by using the setuptools ``python setup.py develop`` method.
|
||||
pip install -e . # install package using setup.py in editable mode
|
||||
|
||||
You can run your tests by pointing to it::
|
||||
- **avoid "__init__.py" files in your test directories**.
|
||||
This way your tests can run easily against an installed version
|
||||
of ``mypkg``, independently from the installed package if it contains
|
||||
the tests or not.
|
||||
|
||||
- With inlined tests you might put ``__init__.py`` into test
|
||||
directories and make them installable as part of your application.
|
||||
Using the ``py.test --pyargs mypkg`` invocation pytest will
|
||||
discover where mypkg is installed and collect tests from there.
|
||||
With the "external" test you can still distribute tests but they
|
||||
will not be installed or become importable.
|
||||
|
||||
Typically you can run tests by pointing to test directories or modules::
|
||||
|
||||
py.test tests/test_app.py # for external test dirs
|
||||
py.test mypkg/test/test_app.py # for inlined test dirs
|
||||
@@ -181,18 +86,34 @@ You can run your tests by pointing to it::
|
||||
py.test # run all tests below current dir
|
||||
...
|
||||
|
||||
.. _`package name`:
|
||||
Because of the above ``editable install`` mode you can change your
|
||||
source code (both tests and the app) and rerun tests at will.
|
||||
Once you are done with your work, you can `use tox`_ to make sure
|
||||
that the package is really correct and tests pass in all
|
||||
required configurations.
|
||||
|
||||
.. note::
|
||||
|
||||
If py.test finds a "a/b/test_module.py" test file while
|
||||
You can use Python3 namespace packages (PEP420) for your application
|
||||
but pytest will still perform `test package name`_ discovery based on the
|
||||
presence of ``__init__.py`` files. If you use one of the
|
||||
two recommended file system layouts above but leave away the ``__init__.py``
|
||||
files from your directories it should just work on Python3.3 and above. From
|
||||
"inlined tests", however, you will need to use absolute imports for
|
||||
getting at your application code.
|
||||
|
||||
.. _`test package name`:
|
||||
|
||||
.. note::
|
||||
|
||||
If ``pytest`` finds a "a/b/test_module.py" test file while
|
||||
recursing into the filesystem it determines the import name
|
||||
as follows:
|
||||
|
||||
* find ``basedir`` -- this is the first "upward" (towards the root)
|
||||
directory not containing an ``__init__.py``. If both the ``a``
|
||||
and ``b`` directories contain an ``__init__.py`` the basedir will
|
||||
be the parent dir of ``a``.
|
||||
* determine ``basedir``: this is the first "upward" (towards the root)
|
||||
directory not containing an ``__init__.py``. If e.g. both ``a``
|
||||
and ``b`` contain an ``__init__.py`` file then the parent directory
|
||||
of ``a`` will become the ``basedir``.
|
||||
|
||||
* perform ``sys.path.insert(0, basedir)`` to make the test module
|
||||
importable under the fully qualified import name.
|
||||
@@ -208,4 +129,160 @@ You can run your tests by pointing to it::
|
||||
to avoid surprises such as a test modules getting imported twice.
|
||||
|
||||
|
||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||
.. _`buildout`: http://www.buildout.org/
|
||||
.. _pip: http://pypi.python.org/pypi/pip
|
||||
|
||||
.. _`use tox`:
|
||||
|
||||
Use tox and Continuous Integration servers
|
||||
-------------------------------------------------
|
||||
|
||||
If you frequently release code and want to make sure that your actual
|
||||
package passes all tests you may want to look into `tox`_, the
|
||||
virtualenv test automation tool and its `pytest support
|
||||
<http://testrun.org/tox/latest/example/pytest.html>`_.
|
||||
Tox helps you to setup virtualenv environments with pre-defined
|
||||
dependencies and then executing a pre-configured test command with
|
||||
options. It will run tests against the installed package and not
|
||||
against your source code checkout, helping to detect packaging
|
||||
glitches.
|
||||
|
||||
If you want to use Jenkins_ you can use the ``--junitxml=PATH`` option
|
||||
to create a JUnitXML file that Jenkins_ can pick up and generate reports.
|
||||
|
||||
.. _standalone:
|
||||
.. _`genscript method`:
|
||||
|
||||
Create a pytest standalone script
|
||||
-------------------------------------------
|
||||
|
||||
If you are a maintainer or application developer and want people
|
||||
who don't deal with python much to easily run tests you may generate
|
||||
a standalone ``pytest`` script::
|
||||
|
||||
py.test --genscript=runtests.py
|
||||
|
||||
This generates a ``runtests.py`` script which is a fully functional basic
|
||||
``pytest`` script, running unchanged under Python2 and Python3.
|
||||
You can tell people to download the script and then e.g. run it like this::
|
||||
|
||||
python runtests.py
|
||||
|
||||
|
||||
Integrating with distutils / ``python setup.py test``
|
||||
--------------------------------------------------------
|
||||
|
||||
You can integrate test runs into your distutils or
|
||||
setuptools based project. Use the `genscript method`_
|
||||
to generate a standalone ``pytest`` script::
|
||||
|
||||
py.test --genscript=runtests.py
|
||||
|
||||
and make this script part of your distribution and then add
|
||||
this to your ``setup.py`` file::
|
||||
|
||||
from distutils.core import setup, Command
|
||||
# you can also import from setuptools
|
||||
|
||||
class PyTest(Command):
|
||||
user_options = []
|
||||
def initialize_options(self):
|
||||
pass
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
import sys,subprocess
|
||||
errno = subprocess.call([sys.executable, 'runtests.py'])
|
||||
raise SystemExit(errno)
|
||||
|
||||
|
||||
setup(
|
||||
#...,
|
||||
cmdclass = {'test': PyTest},
|
||||
#...,
|
||||
)
|
||||
|
||||
If you now type::
|
||||
|
||||
python setup.py test
|
||||
|
||||
this will execute your tests using ``runtests.py``. As this is a
|
||||
standalone version of ``pytest`` no prior installation whatsoever is
|
||||
required for calling the test command. You can also pass additional
|
||||
arguments to the subprocess-calls such as your test directory or other
|
||||
options.
|
||||
|
||||
|
||||
Integration with setuptools test commands
|
||||
----------------------------------------------------
|
||||
|
||||
Setuptools supports writing our own Test command for invoking pytest.
|
||||
Most often it is better to use tox_ instead, but here is how you can
|
||||
get started with setuptools integration::
|
||||
|
||||
import sys
|
||||
|
||||
from setuptools.command.test import test as TestCommand
|
||||
|
||||
|
||||
class PyTest(TestCommand):
|
||||
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
|
||||
|
||||
def initialize_options(self):
|
||||
TestCommand.initialize_options(self)
|
||||
self.pytest_args = None
|
||||
|
||||
def finalize_options(self):
|
||||
TestCommand.finalize_options(self)
|
||||
self.test_args = []
|
||||
self.test_suite = True
|
||||
|
||||
def run_tests(self):
|
||||
#import here, cause outside the eggs aren't loaded
|
||||
import pytest
|
||||
errno = pytest.main(self.pytest_args)
|
||||
sys.exit(errno)
|
||||
|
||||
|
||||
setup(
|
||||
#...,
|
||||
tests_require=['pytest'],
|
||||
cmdclass = {'test': PyTest},
|
||||
)
|
||||
|
||||
Now if you run::
|
||||
|
||||
python setup.py test
|
||||
|
||||
this will download ``pytest`` if needed and then run your tests
|
||||
as you would expect it to. You can pass a single string of arguments
|
||||
using the ``--pytest-args`` or ``-a`` command-line option. For example::
|
||||
|
||||
python setup.py test -a "--durations=5"
|
||||
|
||||
is equivalent to running ``py.test --durations=5``.
|
||||
|
||||
.. _`test discovery`:
|
||||
.. _`Python test discovery`:
|
||||
|
||||
Conventions for Python test discovery
|
||||
-------------------------------------------------
|
||||
|
||||
``pytest`` implements the following standard test discovery:
|
||||
|
||||
* collection starts from the initial command line arguments
|
||||
which may be directories, filenames or test ids.
|
||||
* recurse into directories, unless they match :confval:`norecursedirs`
|
||||
* ``test_*.py`` or ``*_test.py`` files, imported by their `package name`_.
|
||||
* ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
* ``test_`` prefixed test functions or methods are test items
|
||||
|
||||
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
|
||||
|
||||
Within Python modules, ``pytest`` also discovers tests using the standard
|
||||
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
BIN
doc/en/img/pullrequest.png
Normal file
BIN
doc/en/img/pullrequest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
doc/en/img/pytest1.png
Normal file
BIN
doc/en/img/pytest1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
doc/en/img/pytest1favi.ico
Normal file
BIN
doc/en/img/pytest1favi.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -1,52 +1,56 @@
|
||||
|
||||
.. _features:
|
||||
|
||||
.. note:: second training: `professional testing with Python <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_ , 25-27th November 2013, Leipzig.
|
||||
.. note::
|
||||
|
||||
`improving your automated testing with pytest <https://ep2014.europython.eu/en/schedule/sessions/92/>`_, July 25th 2014, Berlin, Germany
|
||||
|
||||
`professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 24-26th November 2014, Freiburg, Germany
|
||||
|
||||
|
||||
pytest: helps you write better programs
|
||||
=============================================
|
||||
|
||||
**a mature full-featured Python testing tool**
|
||||
|
||||
- runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1
|
||||
- runs on Posix/Windows, Python 2.5-3.4, PyPy and Jython-2.5.1
|
||||
- **well tested** with more than a thousand tests against itself
|
||||
- **strict backward compatibility policy** for safe pytest upgrades
|
||||
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
|
||||
- many :ref:`third party plugins <extplugins>` and
|
||||
:ref:`builtin helpers <pytest helpers>`
|
||||
- used in :ref:`many projects and organisations <projects>`, in test
|
||||
suites with up to twenty thousand tests
|
||||
- strict policy of remaining backward compatible across releases
|
||||
- many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
|
||||
- used in :ref:`many small and large projects and organisations <projects>`
|
||||
- comes with many :ref:`tested examples <examples>`
|
||||
|
||||
**provides easy no-boilerplate testing**
|
||||
|
||||
- makes it :ref:`easy to get started <getstarted>`,
|
||||
many :ref:`usage options <usage>`
|
||||
- makes it :ref:`easy to get started <getstarted>`,
|
||||
has many :ref:`usage options <usage>`
|
||||
- :ref:`assert with the assert statement`
|
||||
- helpful :ref:`traceback and failing assertion reporting <tbreportdemo>`
|
||||
- allows :ref:`print debugging <printdebugging>` and :ref:`the
|
||||
- :ref:`print debugging <printdebugging>` and :ref:`the
|
||||
capturing of standard output during test execution <captures>`
|
||||
|
||||
**scales from simple unit to complex functional testing**
|
||||
|
||||
- :ref:`modular parametrizeable fixtures <fixture>` (new in 2.3,
|
||||
improved in 2.4)
|
||||
continously improved)
|
||||
- :ref:`parametrized test functions <parametrized test functions>`
|
||||
- :ref:`mark`
|
||||
- :ref:`skipping` (improved in 2.4)
|
||||
- can :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
|
||||
- can :ref:`continuously re-run failing tests <looponfailing>`
|
||||
- :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
|
||||
- :ref:`continuously re-run failing tests <looponfailing>`
|
||||
- flexible :ref:`Python test discovery`
|
||||
|
||||
**integrates many common testing methods**:
|
||||
**integrates with other testing methods and tools**:
|
||||
|
||||
- multi-paradigm: pytest can run many ``nose``, ``unittest.py`` and
|
||||
``doctest.py`` style test suites, including running testcases made for
|
||||
- multi-paradigm: pytest can run ``nose``, ``unittest`` and
|
||||
``doctest`` style test suites, including running testcases made for
|
||||
Django and trial
|
||||
- supports :ref:`good integration practises <goodpractises>`
|
||||
- supports extended :ref:`xUnit style setup <xunitsetup>`
|
||||
- supports domain-specific :ref:`non-python tests`
|
||||
- supports the generation of testing coverage reports
|
||||
- `Javascript unit- and functional testing`_
|
||||
- supports generating `test coverage reports
|
||||
<https://pypi.python.org/pypi/pytest-cov>`_
|
||||
- supports :pep:`8` compliant coding styles in tests
|
||||
|
||||
**extensive plugin and customization system**:
|
||||
@@ -56,8 +60,6 @@ pytest: helps you write better programs
|
||||
- it is easy to add command line options or customize existing behaviour
|
||||
|
||||
|
||||
.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit
|
||||
|
||||
.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Running tests written for nose
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
py.test has basic support for running tests written for nose_.
|
||||
``pytest`` has basic support for running tests written for nose_.
|
||||
|
||||
.. _nosestyle:
|
||||
|
||||
@@ -16,7 +16,7 @@ After :ref:`installation` type::
|
||||
py.test # instead of 'nosetests'
|
||||
|
||||
and you should be able to run your nose style tests and
|
||||
make use of py.test's capabilities.
|
||||
make use of pytest's capabilities.
|
||||
|
||||
Supported nose Idioms
|
||||
----------------------
|
||||
@@ -25,14 +25,31 @@ Supported nose Idioms
|
||||
* SkipTest exceptions and markers
|
||||
* setup/teardown decorators
|
||||
* yield-based tests and their setup
|
||||
* ``__test__`` attribute on modules/classes/functions
|
||||
* general usage of nose utilities
|
||||
|
||||
Unsupported idioms / known issues
|
||||
----------------------------------
|
||||
|
||||
- unittest-style ``setUp, tearDown, setUpClass, tearDownClass``
|
||||
are recognized only on ``unittest.TestCase`` classes but not
|
||||
on plain classes. ``nose`` supports these methods also on plain
|
||||
classes but pytest deliberately does not. As nose and pytest already
|
||||
both support ``setup_class, teardown_class, setup_method, teardown_method``
|
||||
it doesn't seem useful to duplicate the unittest-API like nose does.
|
||||
If you however rather think pytest should support the unittest-spelling on
|
||||
plain classes please post `to this issue
|
||||
<https://bitbucket.org/hpk42/pytest/issue/377/>`_.
|
||||
|
||||
- nose imports test modules with the same import path (e.g.
|
||||
``tests.test_mod``) but different file system paths
|
||||
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
||||
by extending sys.path/import semantics. pytest does not do that
|
||||
but there is discussion in `issue268 <https://bitbucket.org/hpk42/pytest/issue/268>`_ for adding some support. Note that
|
||||
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.org/en/latest/differences.html#test-discovery-and-loading>`_.
|
||||
|
||||
- nose-style doctests are not collected and executed correctly,
|
||||
also doctest fixtures don't work.
|
||||
|
||||
- no nose-configuration is recognized
|
||||
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ pytest supports test parametrization in several well-integrated ways:
|
||||
at the level of fixture functions <fixture-parametrize>`.
|
||||
|
||||
* `@pytest.mark.parametrize`_ allows to define parametrization at the
|
||||
function or class level, provides multiple argument/fixture sets
|
||||
function or class level, provides multiple argument/fixture sets
|
||||
for a particular test function or class.
|
||||
|
||||
* `pytest_generate_tests`_ enables implementing your own custom
|
||||
* `pytest_generate_tests`_ enables implementing your own custom
|
||||
dynamic parametrization scheme or extensions.
|
||||
|
||||
.. _parametrizemark:
|
||||
@@ -47,13 +47,13 @@ to an expected output::
|
||||
def test_eval(input, expected):
|
||||
assert eval(input) == expected
|
||||
|
||||
Here, the ``@parametrize`` decorator defines three different ``(input,output)``
|
||||
tuples so that that the ``test_eval`` function will run three times using
|
||||
Here, the ``@parametrize`` decorator defines three different ``(input,expected)``
|
||||
tuples so that the ``test_eval`` function will run three times using
|
||||
them in turn::
|
||||
|
||||
$ py.test
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 3 items
|
||||
|
||||
test_expectation.py ..F
|
||||
@@ -100,7 +100,7 @@ Let's run this::
|
||||
|
||||
$ py.test
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 3 items
|
||||
|
||||
test_expectation.py ..x
|
||||
@@ -114,8 +114,8 @@ shows up as an "xfailed (expected to fail)" test.
|
||||
|
||||
In versions prior to 2.4 one needed to specify the argument
|
||||
names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
|
||||
comma-separated-string syntax is now advertised fist because
|
||||
it's easier to write, produces less line noise.
|
||||
comma-separated-string syntax is now advertised first because
|
||||
it's easier to write and produces less line noise.
|
||||
|
||||
.. _`pytest_generate_tests`:
|
||||
|
||||
@@ -124,14 +124,14 @@ Basic ``pytest_generate_tests`` example
|
||||
|
||||
Sometimes you may want to implement your own parametrization scheme
|
||||
or implement some dynamism for determining the parameters or scope
|
||||
of a fixture. For this, you can use the ``pytest_generate_tests`` hook
|
||||
of a fixture. For this, you can use the ``pytest_generate_tests`` hook
|
||||
which is called when collecting a test function. Through the passed in
|
||||
`metafunc` object you can inspect the requesting test context and, most
|
||||
importantly, you can call ``metafunc.parametrize()`` to cause
|
||||
parametrization.
|
||||
parametrization.
|
||||
|
||||
For example, let's say we want to run a test taking string inputs which
|
||||
we want to set via a new py.test command line option. Let's first write
|
||||
we want to set via a new ``pytest`` command line option. Let's first write
|
||||
a simple test accepting a ``stringinput`` fixture function argument::
|
||||
|
||||
# content of test_strings.py
|
||||
@@ -139,7 +139,7 @@ a simple test accepting a ``stringinput`` fixture function argument::
|
||||
def test_valid_string(stringinput):
|
||||
assert stringinput.isalpha()
|
||||
|
||||
Now we add a ``conftest.py`` file containing the addition of a
|
||||
Now we add a ``conftest.py`` file containing the addition of a
|
||||
command line option and the parametrization of our test function::
|
||||
|
||||
# content of conftest.py
|
||||
@@ -150,7 +150,7 @@ command line option and the parametrization of our test function::
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'stringinput' in metafunc.fixturenames:
|
||||
metafunc.parametrize("stringinput",
|
||||
metafunc.parametrize("stringinput",
|
||||
metafunc.config.option.stringinput)
|
||||
|
||||
If we now pass two stringinput values, our test will run twice::
|
||||
@@ -170,22 +170,22 @@ Let's also run with a stringinput that will lead to a failing test::
|
||||
|
||||
def test_valid_string(stringinput):
|
||||
> assert stringinput.isalpha()
|
||||
E assert <built-in method isalpha of str object at 0x2ac85b043198>()
|
||||
E + where <built-in method isalpha of str object at 0x2ac85b043198> = '!'.isalpha
|
||||
E assert <built-in method isalpha of str object at 0x2b7e70b5d210>()
|
||||
E + where <built-in method isalpha of str object at 0x2b7e70b5d210> = '!'.isalpha
|
||||
|
||||
test_strings.py:3: AssertionError
|
||||
1 failed in 0.01 seconds
|
||||
|
||||
As expected our test function fails.
|
||||
As expected our test function fails.
|
||||
|
||||
If you don't specify a stringinput it will be skipped because
|
||||
``metafunc.parametrize()`` will be called with an empty parameter
|
||||
listlist::
|
||||
|
||||
$ py.test -q -rs test_strings.py
|
||||
$ py.test -q -rs test_strings.py
|
||||
s
|
||||
========================= short test summary info ==========================
|
||||
SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:1024: got empty parameter set, function test_valid_string at /tmp/doc-exec-561/test_strings.py:1
|
||||
SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:1139: got empty parameter set, function test_valid_string at /tmp/doc-exec-100/test_strings.py:1
|
||||
1 skipped in 0.01 seconds
|
||||
|
||||
For further examples, you might want to look at :ref:`more
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
Working with plugins and conftest files
|
||||
=============================================
|
||||
|
||||
py.test implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic location types:
|
||||
``pytest`` implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic location types:
|
||||
|
||||
* `builtin plugins`_: loaded from py.test's internal ``_pytest`` directory.
|
||||
* `builtin plugins`_: loaded from pytest's internal ``_pytest`` directory.
|
||||
* `external plugins`_: modules discovered through `setuptools entry points`_
|
||||
* `conftest.py plugins`_: modules auto-discovered in test directories
|
||||
|
||||
@@ -63,8 +63,11 @@ tool, for example::
|
||||
pip install pytest-NAME
|
||||
pip uninstall pytest-NAME
|
||||
|
||||
If a plugin is installed, py.test automatically finds and integrates it,
|
||||
there is no need to activate it. Here is a initial list of known plugins:
|
||||
If a plugin is installed, ``pytest`` automatically finds and integrates it,
|
||||
there is no need to activate it. We have a :doc:`page listing
|
||||
all 3rd party plugins and their status against the latest py.test version
|
||||
<plugins_index/index>` and here is a little annotated list
|
||||
for some popular plugins:
|
||||
|
||||
.. _`django`: https://www.djangoproject.com/
|
||||
|
||||
@@ -84,14 +87,14 @@ there is no need to activate it. Here is a initial list of known plugins:
|
||||
* `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_:
|
||||
to distribute tests to CPUs and remote hosts, to run in boxed
|
||||
mode which allows to survive segmentation faults, to run in
|
||||
looponfailing mode, automatically re-running failing tests
|
||||
looponfailing mode, automatically re-running failing tests
|
||||
on file changes, see also :ref:`xdist`
|
||||
|
||||
* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_:
|
||||
to report failures while the test run is happening.
|
||||
|
||||
* `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ and
|
||||
`pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
|
||||
`pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
|
||||
to write tests using behaviour-driven testing.
|
||||
|
||||
* `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_:
|
||||
@@ -107,7 +110,11 @@ there is no need to activate it. Here is a initial list of known plugins:
|
||||
* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
|
||||
a plugin to run javascript unittests in life browsers
|
||||
|
||||
You may discover more plugins through a `pytest- pypi.python.org search`_.
|
||||
To see a complete list of all plugins with their latest testing
|
||||
status against different py.test and Python versions, please visit
|
||||
`pytest-plugs <http://pytest-plugs.herokuapp.com/>`_.
|
||||
|
||||
You may also discover more plugins through a `pytest- pypi.python.org search`_.
|
||||
|
||||
.. _`available installable plugins`:
|
||||
.. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search
|
||||
@@ -122,7 +129,7 @@ If you want to write a plugin, there are many real-life examples
|
||||
you can copy from:
|
||||
|
||||
* a custom collection example plugin: :ref:`yaml plugin`
|
||||
* around 20 `builtin plugins`_ which provide py.test's own functionality
|
||||
* around 20 `builtin plugins`_ which provide pytest's own functionality
|
||||
* many `external plugins`_ providing additional features
|
||||
|
||||
All of these plugins implement the documented `well specified hooks`_
|
||||
@@ -135,10 +142,10 @@ Making your plugin installable by others
|
||||
|
||||
If you want to make your plugin externally available, you
|
||||
may define a so-called entry point for your distribution so
|
||||
that ``py.test`` finds your plugin module. Entry points are
|
||||
that ``pytest`` finds your plugin module. Entry points are
|
||||
a feature that is provided by `setuptools`_ or `Distribute`_.
|
||||
py.test looks up the ``pytest11`` entrypoint to discover its
|
||||
plugins and you can thus make your plugin available by definig
|
||||
pytest looks up the ``pytest11`` entrypoint to discover its
|
||||
plugins and you can thus make your plugin available by defining
|
||||
it in your setuptools/distribute-based setup-invocation:
|
||||
|
||||
.. sourcecode:: python
|
||||
@@ -150,7 +157,7 @@ it in your setuptools/distribute-based setup-invocation:
|
||||
name="myproject",
|
||||
packages = ['myproject']
|
||||
|
||||
# the following makes a plugin available to py.test
|
||||
# the following makes a plugin available to pytest
|
||||
entry_points = {
|
||||
'pytest11': [
|
||||
'name_of_plugin = myproject.pluginmodule',
|
||||
@@ -158,7 +165,7 @@ it in your setuptools/distribute-based setup-invocation:
|
||||
},
|
||||
)
|
||||
|
||||
If a package is installed this way, py.test will load
|
||||
If a package is installed this way, ``pytest`` will load
|
||||
``myproject.pluginmodule`` as a plugin which can define
|
||||
`well specified hooks`_.
|
||||
|
||||
@@ -167,7 +174,7 @@ If a package is installed this way, py.test will load
|
||||
Plugin discovery order at tool startup
|
||||
--------------------------------------------
|
||||
|
||||
py.test loads plugin modules at tool startup in the following way:
|
||||
``pytest`` loads plugin modules at tool startup in the following way:
|
||||
|
||||
* by loading all builtin plugins
|
||||
|
||||
@@ -177,9 +184,15 @@ 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 :file:`conftest.py` files as inferred by the command line
|
||||
invocation (test files and all of its *parent* directories).
|
||||
Note that ``conftest.py`` files from *sub* directories are by default
|
||||
not loaded at tool startup.
|
||||
invocation:
|
||||
|
||||
- if no test paths are specified use current dir as a test path
|
||||
- if exists, load ``conftest.py`` and ``test*/conftest.py`` relative
|
||||
to the directory part of the first test path.
|
||||
|
||||
Note that pytest does not find ``conftest.py`` files in deeper nested
|
||||
sub directories at tool startup. It is usually a good idea to keep
|
||||
your conftest.py file in the top level test or project root directory.
|
||||
|
||||
* by recursively loading all plugins specified by the
|
||||
``pytest_plugins`` variable in ``conftest.py`` files
|
||||
@@ -197,7 +210,7 @@ will be loaded as well. You can also use dotted path like this::
|
||||
|
||||
pytest_plugins = "myapp.testsupport.myplugin"
|
||||
|
||||
which will import the specified module as a py.test plugin.
|
||||
which will import the specified module as a ``pytest`` plugin.
|
||||
|
||||
|
||||
Accessing another plugin by name
|
||||
@@ -243,7 +256,7 @@ how to obtain the name of a plugin.
|
||||
|
||||
.. _`builtin plugins`:
|
||||
|
||||
py.test default plugin reference
|
||||
pytest default plugin reference
|
||||
====================================
|
||||
|
||||
|
||||
@@ -277,14 +290,14 @@ in the `pytest repository <http://bitbucket.org/hpk42/pytest/>`_.
|
||||
|
||||
.. _`well specified hooks`:
|
||||
|
||||
py.test hook reference
|
||||
pytest hook reference
|
||||
====================================
|
||||
|
||||
Hook specification and validation
|
||||
-----------------------------------------
|
||||
|
||||
py.test calls hook functions to implement initialization, running,
|
||||
test execution and reporting. When py.test loads a plugin it validates
|
||||
``pytest`` calls hook functions to implement initialization, running,
|
||||
test execution and reporting. When ``pytest`` loads a plugin it validates
|
||||
that each hook function conforms to its respective hook specification.
|
||||
Each hook function name and its argument names need to match a hook
|
||||
specification. However, a hook function may accept *fewer* parameters
|
||||
@@ -296,6 +309,7 @@ Initialization, command line and configuration hooks
|
||||
|
||||
.. currentmodule:: _pytest.hookspec
|
||||
|
||||
.. autofunction:: pytest_load_initial_conftests
|
||||
.. autofunction:: pytest_cmdline_preparse
|
||||
.. autofunction:: pytest_cmdline_parse
|
||||
.. autofunction:: pytest_namespace
|
||||
@@ -307,7 +321,7 @@ Initialization, command line and configuration hooks
|
||||
Generic "runtest" hooks
|
||||
------------------------------
|
||||
|
||||
All all runtest related hooks receive a :py:class:`pytest.Item` object.
|
||||
All runtest related hooks receive a :py:class:`pytest.Item` object.
|
||||
|
||||
.. autofunction:: pytest_runtest_protocol
|
||||
.. autofunction:: pytest_runtest_setup
|
||||
@@ -327,7 +341,7 @@ the reporting hook to print information about a test run.
|
||||
Collection hooks
|
||||
------------------------------
|
||||
|
||||
py.test calls the following hooks for collecting files and directories:
|
||||
``pytest`` calls the following hooks for collecting files and directories:
|
||||
|
||||
.. autofunction:: pytest_ignore_collect
|
||||
.. autofunction:: pytest_collect_directory
|
||||
|
||||
BIN
doc/en/plugins_index/bitbucket.png
Normal file
BIN
doc/en/plugins_index/bitbucket.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 679 B |
BIN
doc/en/plugins_index/github.png
Normal file
BIN
doc/en/plugins_index/github.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 734 B |
157
doc/en/plugins_index/index.txt
Normal file
157
doc/en/plugins_index/index.txt
Normal file
@@ -0,0 +1,157 @@
|
||||
.. _plugins_index:
|
||||
|
||||
List of Third-Party Plugins
|
||||
===========================
|
||||
|
||||
The table below contains a listing of plugins found in PyPI and
|
||||
their status when tested using py.test **2.6.0** and python 2.7 and
|
||||
3.3.
|
||||
|
||||
A complete listing can also be found at
|
||||
`pytest-plugs <http://pytest-plugs.herokuapp.com/>`_, which contains tests
|
||||
status against other py.test releases.
|
||||
|
||||
|
||||
========================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= =============================================================================================================================================
|
||||
Name Py27 Py34 Repo Summary
|
||||
========================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= =============================================================================================================================================
|
||||
`pytest-allure-adaptor-1.3.8 <http://pypi.python.org/pypi/pytest-allure-adaptor>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-allure-adaptor-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-allure-adaptor-latest?py=py34&pytest=2.6.0 .. image:: github.png Plugin for py.test to generate allure xml reports
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-allure-adaptor-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-allure-adaptor-latest?py=py34&pytest=2.6.0 :target: https://github.com/allure-framework/allure-python
|
||||
`pytest-bdd-2.1.1 <http://pypi.python.org/pypi/pytest-bdd>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py34&pytest=2.6.0 .. image:: github.png BDD for pytest
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py34&pytest=2.6.0 :target: https://github.com/olegpidsadnyi/pytest-bdd
|
||||
`pytest-beds-0.0.1 <http://pypi.python.org/pypi/pytest-beds>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-beds-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-beds-latest?py=py34&pytest=2.6.0 .. image:: github.png Fixtures for testing Google Appengine (GAE) apps
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-beds-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-beds-latest?py=py34&pytest=2.6.0 :target: https://github.com/kaste/pytest-beds
|
||||
`pytest-bench-0.2.5 <http://pypi.python.org/pypi/pytest-bench>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py34&pytest=2.6.0 .. image:: github.png Benchmark utility that plugs into pytest.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py34&pytest=2.6.0 :target: http://github.com/concordusapps/pytest-bench
|
||||
`pytest-blockage-0.1 <http://pypi.python.org/pypi/pytest-blockage>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py34&pytest=2.6.0 .. image:: github.png Disable network requests during a test run.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py34&pytest=2.6.0 :target: https://github.com/rob-b/pytest-blockage
|
||||
`pytest-browsermob-proxy-0.1 <http://pypi.python.org/pypi/pytest-browsermob-proxy>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py34&pytest=2.6.0 .. image:: github.png BrowserMob proxy plugin for py.test.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py34&pytest=2.6.0 :target: https://github.com/davehunt/pytest-browsermob-proxy
|
||||
`pytest-bugzilla-0.2 <http://pypi.python.org/pypi/pytest-bugzilla>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test bugzilla integration plugin
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py34&pytest=2.6.0 :target: http://github.com/nibrahim/pytest_bugzilla
|
||||
`pytest-cache-1.0 <http://pypi.python.org/pypi/pytest-cache>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest plugin with mechanisms for caching across test runs
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/hpk42/pytest-cache/
|
||||
`pytest-capturelog-0.7 <http://pypi.python.org/pypi/pytest-capturelog>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png py.test plugin to capture log messages
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/memedough/pytest-capturelog/overview
|
||||
`pytest-codecheckers-0.2 <http://pypi.python.org/pypi/pytest-codecheckers>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest plugin to add source code sanity checks (pep8 and friends)
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/
|
||||
`pytest-config-0.0.10 <http://pypi.python.org/pypi/pytest-config>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-config-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-config-latest?py=py34&pytest=2.6.0 .. image:: github.png Base configurations and utilities for developing your Python project test suite with pytest.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-config-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-config-latest?py=py34&pytest=2.6.0 :target: https://github.com/buzzfeed/pytest_config
|
||||
`pytest-contextfixture-0.1.1 <http://pypi.python.org/pypi/pytest-contextfixture>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py34&pytest=2.6.0 .. image:: github.png Define pytest fixtures as context managers.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py34&pytest=2.6.0 :target: http://github.com/pelme/pytest-contextfixture/
|
||||
`pytest-couchdbkit-0.5.1 <http://pypi.python.org/pypi/pytest-couchdbkit>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png py.test extension for per-test couchdb databases using couchdbkit
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit
|
||||
`pytest-cov-1.7.0 <http://pypi.python.org/pypi/pytest-cov>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py34&pytest=2.6.0 :target: https://github.com/schlamar/pytest-cov
|
||||
`pytest-dbfixtures-0.4.20 <http://pypi.python.org/pypi/pytest-dbfixtures>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py34&pytest=2.6.0 .. image:: github.png Databases fixtures plugin for py.test.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py34&pytest=2.6.0 :target: https://github.com/ClearcodeHQ/pytest-dbfixtures
|
||||
`pytest-dbus-notification-1.0.1 <http://pypi.python.org/pypi/pytest-dbus-notification>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py34&pytest=2.6.0 .. image:: github.png D-BUS notifications for pytest results.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py34&pytest=2.6.0 :target: https://github.com/bmathieu33/pytest-dbus-notification
|
||||
`pytest-diffeo-0.1.7 <http://pypi.python.org/pypi/pytest-diffeo>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-diffeo-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-diffeo-latest?py=py34&pytest=2.6.0 .. image:: github.png Common py.test support for Diffeo packages
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-diffeo-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-diffeo-latest?py=py34&pytest=2.6.0 :target: https://github.com/diffeo/pytest-diffeo
|
||||
`pytest-django-2.6.2 <http://pypi.python.org/pypi/pytest-django>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py34&pytest=2.6.0 `link <http://pytest-django.readthedocs.org/>`_ A Django plugin for py.test.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py34&pytest=2.6.0
|
||||
`pytest-django-haystack-0.1.1 <http://pypi.python.org/pypi/pytest-django-haystack>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-haystack-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-haystack-latest?py=py34&pytest=2.6.0 .. image:: github.png Cleanup your Haystack indexes between tests
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-django-haystack-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-haystack-latest?py=py34&pytest=2.6.0 :target: http://github.com/rouge8/pytest-django-haystack
|
||||
`pytest-django-lite-0.1.1 <http://pypi.python.org/pypi/pytest-django-lite>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py34&pytest=2.6.0 .. image:: github.png The bare minimum to integrate py.test with Django.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py34&pytest=2.6.0 :target: https://github.com/dcramer/pytest-django-lite
|
||||
`pytest-eradicate-0.0.2 <http://pypi.python.org/pypi/pytest-eradicate>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-eradicate-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-eradicate-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin to check for commented out code
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-eradicate-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-eradicate-latest?py=py34&pytest=2.6.0 :target: https://github.com/spil-johan/pytest-eradicate
|
||||
`pytest-figleaf-1.0 <http://pypi.python.org/pypi/pytest-figleaf>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png py.test figleaf coverage plugin
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/hpk42/pytest-figleaf
|
||||
`pytest-flakes-0.2 <http://pypi.python.org/pypi/pytest-flakes>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin to check source code with pyflakes
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py34&pytest=2.6.0 :target: https://github.com/fschulze/pytest-flakes
|
||||
`pytest-greendots-0.3 <http://pypi.python.org/pypi/pytest-greendots>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py34&pytest=2.6.0 ? Green progress dots
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py34&pytest=2.6.0
|
||||
`pytest-growl-0.2 <http://pypi.python.org/pypi/pytest-growl>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py34&pytest=2.6.0 ? Growl notifications for pytest results.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py34&pytest=2.6.0
|
||||
`pytest-httpbin-0.0.2 <http://pypi.python.org/pypi/pytest-httpbin>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpbin-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpbin-latest?py=py34&pytest=2.6.0 .. image:: github.png Easily test your HTTP library against a local copy of httpbin
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-httpbin-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-httpbin-latest?py=py34&pytest=2.6.0 :target: https://github.com/kevin1024/pytest-httpbin
|
||||
`pytest-httpretty-0.2.0 <http://pypi.python.org/pypi/pytest-httpretty>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py34&pytest=2.6.0 .. image:: github.png A thin wrapper of HTTPretty for pytest
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py34&pytest=2.6.0 :target: http://github.com/papaeye/pytest-httpretty
|
||||
`pytest-incremental-0.3.0 <http://pypi.python.org/pypi/pytest-incremental>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png an incremental test runner (pytest plugin)
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py34&pytest=2.6.0 :target: https://bitbucket.org/schettino72/pytest-incremental
|
||||
`pytest-instafail-0.2.0 <http://pypi.python.org/pypi/pytest-instafail>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test plugin to show failures instantly
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py34&pytest=2.6.0 :target: https://github.com/jpvanhal/pytest-instafail
|
||||
`pytest-ipdb-0.1-prerelease <http://pypi.python.org/pypi/pytest-ipdb>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py34&pytest=2.6.0 .. image:: github.png A py.test plug-in to enable drop to ipdb debugger on test failure.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py34&pytest=2.6.0 :target: https://github.com/mverteuil/pytest-ipdb
|
||||
`pytest-jira-0.01 <http://pypi.python.org/pypi/pytest-jira>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test JIRA integration plugin, using markers
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py34&pytest=2.6.0 :target: http://github.com/jlaska/pytest_jira
|
||||
`pytest-knows-0.1.0 <http://pypi.python.org/pypi/pytest-knows>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-knows-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-knows-latest?py=py34&pytest=2.6.0 .. image:: github.png A pytest plugin that can automaticly skip test case based on dependence info calculated by trace
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-knows-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-knows-latest?py=py34&pytest=2.6.0 :target: https://github.com/mapix/ptknows
|
||||
`pytest-konira-0.2 <http://pypi.python.org/pypi/pytest-konira>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py34&pytest=2.6.0 .. image:: github.png Run Konira DSL tests with py.test
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py34&pytest=2.6.0 :target: http://github.com/alfredodeza/pytest-konira
|
||||
`pytest-localserver-0.3.2 <http://pypi.python.org/pypi/pytest-localserver>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png py.test plugin to test server connections locally.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/basti/pytest-localserver/
|
||||
`pytest-marker-bugzilla-0.06 <http://pypi.python.org/pypi/pytest-marker-bugzilla>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test bugzilla integration plugin, using markers
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py34&pytest=2.6.0 :target: http://github.com/eanxgeek/pytest_marker_bugzilla
|
||||
`pytest-markfiltration-0.8 <http://pypi.python.org/pypi/pytest-markfiltration>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py34&pytest=2.6.0 .. image:: github.png UNKNOWN
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py34&pytest=2.6.0 :target: https://github.com/adamgoucher/pytest-markfiltration
|
||||
`pytest-marks-0.4 <http://pypi.python.org/pypi/pytest-marks>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py34&pytest=2.6.0 .. image:: github.png UNKNOWN
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py34&pytest=2.6.0 :target: https://github.com/adamgoucher/pytest-marks
|
||||
`pytest-mock-0.2.0 <http://pypi.python.org/pypi/pytest-mock>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mock-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mock-latest?py=py34&pytest=2.6.0 .. image:: github.png Thin-wrapper around the mock package for easier use with py.test
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-mock-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-mock-latest?py=py34&pytest=2.6.0 :target: https://github.com/nicoddemus/pytest-mock/
|
||||
`pytest-monkeyplus-1.1.0 <http://pypi.python.org/pypi/pytest-monkeyplus>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest's monkeypatch subclass with extra functionalities
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/hsoft/pytest-monkeyplus/
|
||||
`pytest-mozwebqa-1.1.1 <http://pypi.python.org/pypi/pytest-mozwebqa>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py34&pytest=2.6.0 .. image:: github.png Mozilla WebQA plugin for py.test.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py34&pytest=2.6.0 :target: https://github.com/davehunt/pytest-mozwebqa
|
||||
`pytest-oerp-0.2.0 <http://pypi.python.org/pypi/pytest-oerp>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin to test OpenERP modules
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py34&pytest=2.6.0 :target: http://github.com/santagada/pytest-oerp/
|
||||
`pytest-ordering-0.3 <http://pypi.python.org/pypi/pytest-ordering>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ordering-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ordering-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin to run your tests in a specific order
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-ordering-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-ordering-latest?py=py34&pytest=2.6.0 :target: https://github.com/ftobia/pytest-ordering
|
||||
`pytest-osxnotify-0.1.4 <http://pypi.python.org/pypi/pytest-osxnotify>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py34&pytest=2.6.0 .. image:: github.png OS X notifications for py.test results.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py34&pytest=2.6.0 :target: https://github.com/dbader/pytest-osxnotify
|
||||
`pytest-paste-config-0.1 <http://pypi.python.org/pypi/pytest-paste-config>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py34&pytest=2.6.0 ? Allow setting the path to a paste config file
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py34&pytest=2.6.0
|
||||
`pytest-pep8-1.0.6 <http://pypi.python.org/pypi/pytest-pep8>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest plugin to check PEP8 requirements
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/hpk42/pytest-pep8/
|
||||
`pytest-poo-0.2 <http://pypi.python.org/pypi/pytest-poo>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py34&pytest=2.6.0 .. image:: github.png Visualize your crappy tests
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py34&pytest=2.6.0 :target: http://github.com/pelme/pytest-poo
|
||||
`pytest-pycharm-0.1.0 <http://pypi.python.org/pypi/pytest-pycharm>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pycharm-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pycharm-latest?py=py34&pytest=2.6.0 .. image:: github.png Plugin for py.test to enter PyCharm debugger on uncaught exceptions
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-pycharm-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-pycharm-latest?py=py34&pytest=2.6.0 :target: https://github.com/jlubcke/pytest-pycharm
|
||||
`pytest-pydev-0.1 <http://pypi.python.org/pypi/pytest-pydev>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png py.test plugin to connect to a remote debug server with PyDev or PyCharm.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/basti/pytest-pydev/
|
||||
`pytest-pythonpath-0.3 <http://pypi.python.org/pypi/pytest-pythonpath>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin for adding to the PYTHONPATH from command line or configs.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py34&pytest=2.6.0 :target: https://github.com/bigsassy/pytest-pythonpath
|
||||
`pytest-qt-1.2.0 <http://pypi.python.org/pypi/pytest-qt>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest support for PyQt and PySide applications
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py34&pytest=2.6.0 :target: http://github.com/nicoddemus/pytest-qt
|
||||
`pytest-quickcheck-0.8 <http://pypi.python.org/pypi/pytest-quickcheck>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest plugin to generate random data inspired by QuickCheck
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/t2y/pytest-quickcheck/
|
||||
`pytest-rage-0.1 <http://pypi.python.org/pypi/pytest-rage>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin to implement PEP712
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py34&pytest=2.6.0 :target: http://github.com/santagada/pytest-rage/
|
||||
`pytest-raisesregexp-1.0 <http://pypi.python.org/pypi/pytest-raisesregexp>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-raisesregexp-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-raisesregexp-latest?py=py34&pytest=2.6.0 .. image:: github.png Simple pytest plugin to look for regex in Exceptions
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-raisesregexp-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-raisesregexp-latest?py=py34&pytest=2.6.0 :target: https://github.com/Walkman/pytest_raisesregexp
|
||||
`pytest-random-0.02 <http://pypi.python.org/pypi/pytest-random>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test plugin to randomize tests
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py34&pytest=2.6.0 :target: https://github.com/klrmn/pytest-random
|
||||
`pytest-rerunfailures-0.05 <http://pypi.python.org/pypi/pytest-rerunfailures>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test plugin to re-run tests to eliminate flakey failures
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py34&pytest=2.6.0 :target: https://github.com/klrmn/pytest-rerunfailures
|
||||
`pytest-runfailed-0.3 <http://pypi.python.org/pypi/pytest-runfailed>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py34&pytest=2.6.0 .. image:: github.png implement a --failed option for pytest
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py34&pytest=2.6.0 :target: http://github.com/dmerejkowsky/pytest-runfailed
|
||||
`pytest-runner-2.0 <http://pypi.python.org/pypi/pytest-runner>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png UNKNOWN
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py34&pytest=2.6.0 :target: https://bitbucket.org/jaraco/pytest-runner
|
||||
`pytest-sftpserver-1.0.0 <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test plugin to locally test sftp server connections.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.6.0 :target: http://github.com/ulope/pytest-sftpserver/
|
||||
`pytest-spec-0.2.22 <http://pypi.python.org/pypi/pytest-spec>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-spec-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-spec-latest?py=py34&pytest=2.6.0 .. image:: github.png pytest plugin to display test execution output like a SPECIFICATION
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-spec-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-spec-latest?py=py34&pytest=2.6.0 :target: https://github.com/pchomik/pytest-spec
|
||||
`pytest-splinter-1.0.3 <http://pypi.python.org/pypi/pytest-splinter>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-splinter-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-splinter-latest?py=py34&pytest=2.6.0 .. image:: github.png Splinter subplugin for Pytest BDD plugin
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-splinter-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-splinter-latest?py=py34&pytest=2.6.0 :target: https://github.com/paylogic/pytest-splinter
|
||||
`pytest-stepwise-0.1 <http://pypi.python.org/pypi/pytest-stepwise>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-stepwise-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-stepwise-latest?py=py34&pytest=2.6.0 .. image:: github.png Run a test suite one failing test at a time.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-stepwise-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-stepwise-latest?py=py34&pytest=2.6.0 :target: https://github.com/nip3o/pytest-stepwise
|
||||
`pytest-sugar-0.3.4 <http://pypi.python.org/pypi/pytest-sugar>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py34&pytest=2.6.0 .. image:: github.png py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly).
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py34&pytest=2.6.0 :target: https://github.com/Frozenball/pytest-sugar
|
||||
`pytest-timeout-0.3 <http://pypi.python.org/pypi/pytest-timeout>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest plugin to abort tests after a timeout
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/flub/pytest-timeout/
|
||||
`pytest-twisted-1.5 <http://pypi.python.org/pypi/pytest-twisted>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py34&pytest=2.6.0 .. image:: github.png A twisted plugin for py.test.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py34&pytest=2.6.0 :target: https://github.com/schmir/pytest-twisted
|
||||
`pytest-xdist-1.10 <http://pypi.python.org/pypi/pytest-xdist>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png py.test xdist plugin for distributed testing and loop-on-failing modes
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/hpk42/pytest-xdist
|
||||
`pytest-xprocess-0.8 <http://pypi.python.org/pypi/pytest-xprocess>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py34&pytest=2.6.0 .. image:: bitbucket.png pytest plugin to manage external processes across test runs
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py34&pytest=2.6.0 :target: http://bitbucket.org/hpk42/pytest-xprocess/
|
||||
`pytest-yamlwsgi-0.6 <http://pypi.python.org/pypi/pytest-yamlwsgi>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py34&pytest=2.6.0 ? Run tests against wsgi apps defined in yaml
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py34&pytest=2.6.0
|
||||
`pytest-zap-0.2 <http://pypi.python.org/pypi/pytest-zap>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.6.0 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py34&pytest=2.6.0 .. image:: github.png OWASP ZAP plugin for py.test.
|
||||
:target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.6.0 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py34&pytest=2.6.0 :target: https://github.com/davehunt/pytest-zap
|
||||
|
||||
========================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= =============================================================================================================================================
|
||||
|
||||
*(Updated on 2014-07-18)*
|
||||
292
doc/en/plugins_index/plugins_index.py
Normal file
292
doc/en/plugins_index/plugins_index.py
Normal file
@@ -0,0 +1,292 @@
|
||||
"""
|
||||
Script to generate the file `index.txt` with information about
|
||||
pytest plugins taken directly from PyPI.
|
||||
|
||||
Usage:
|
||||
python plugins_index.py
|
||||
|
||||
This command will update `index.txt` in the same directory found as this script.
|
||||
This should be issued before every major documentation release to obtain latest
|
||||
versions from PyPI.
|
||||
|
||||
Also includes plugin compatibility between different python and pytest versions,
|
||||
obtained from http://pytest-plugs.herokuapp.com.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
from collections import namedtuple
|
||||
import datetime
|
||||
from distutils.version import LooseVersion
|
||||
import itertools
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
|
||||
def get_proxy(url):
|
||||
"""
|
||||
wrapper function to obtain a xmlrpc proxy, taking in account import
|
||||
differences between python 2.X and 3.X
|
||||
|
||||
:param url: url to bind the proxy to
|
||||
:return: a ServerProxy instance
|
||||
"""
|
||||
if sys.version_info < (3, 0):
|
||||
from xmlrpclib import ServerProxy
|
||||
else:
|
||||
from xmlrpc.client import ServerProxy
|
||||
return ServerProxy(url)
|
||||
|
||||
|
||||
def iter_plugins(client, search='pytest-'):
|
||||
"""
|
||||
Returns an iterator of (name, version) from PyPI.
|
||||
|
||||
:param client: ServerProxy
|
||||
:param search: package names to search for
|
||||
"""
|
||||
for plug_data in client.search({'name': search}):
|
||||
yield plug_data['name'], plug_data['version']
|
||||
|
||||
|
||||
def get_latest_versions(plugins):
|
||||
"""
|
||||
Returns an iterator of (name, version) from the given list of (name,
|
||||
version), but returning only the latest version of the package. Uses
|
||||
distutils.LooseVersion to ensure compatibility with PEP386.
|
||||
"""
|
||||
plugins = [(name, LooseVersion(version)) for (name, version) in plugins]
|
||||
for name, grouped_plugins in itertools.groupby(plugins, key=lambda x: x[0]):
|
||||
name, loose_version = list(grouped_plugins)[-1]
|
||||
yield name, str(loose_version)
|
||||
|
||||
|
||||
def obtain_plugins_table(plugins, client):
|
||||
"""
|
||||
Returns information to populate a table of plugins, their versions,
|
||||
authors, etc.
|
||||
|
||||
The returned information is a list of columns of `ColumnData`
|
||||
namedtuples(text, link). Link can be None if the text for that column
|
||||
should not be linked to anything.
|
||||
|
||||
:param plugins: list of (name, version)
|
||||
:param client: ServerProxy
|
||||
"""
|
||||
def get_repo_markup(repo):
|
||||
"""
|
||||
obtains appropriate markup for the given repository, as two lines
|
||||
that should be output in the same table row. We use this to display an icon
|
||||
for known repository hosts (github, etc), just a "?" char when
|
||||
repository is not registered in pypi or a simple link otherwise.
|
||||
"""
|
||||
target = repo
|
||||
if 'github.com' in repo:
|
||||
image = 'github.png'
|
||||
elif 'bitbucket.org' in repo:
|
||||
image = 'bitbucket.png'
|
||||
elif repo.lower() == 'unknown':
|
||||
return '?', ''
|
||||
else:
|
||||
image = None
|
||||
|
||||
if image is not None:
|
||||
image_markup = '.. image:: %s' % image
|
||||
target_markup = ' :target: %s' % repo
|
||||
pad_right = ('%-' + str(len(target_markup)) + 's')
|
||||
return pad_right % image_markup, target_markup
|
||||
else:
|
||||
return '`link <%s>`_' % target, ''
|
||||
|
||||
def sanitize_summary(summary):
|
||||
"""Make sure summaries don't break our table formatting.
|
||||
"""
|
||||
return summary.replace('\n', ' ')
|
||||
|
||||
rows = []
|
||||
ColumnData = namedtuple('ColumnData', 'text link')
|
||||
headers = ['Name', 'Py27', 'Py34', 'Repo', 'Summary']
|
||||
pytest_version = pytest.__version__
|
||||
repositories = obtain_override_repositories()
|
||||
print('*** pytest-{0} ***'.format(pytest_version))
|
||||
plugins = list(plugins)
|
||||
for index, (package_name, version) in enumerate(plugins):
|
||||
print(package_name, version, '...', end='')
|
||||
|
||||
release_data = client.release_data(package_name, version)
|
||||
|
||||
common_params = dict(
|
||||
site='http://pytest-plugs.herokuapp.com',
|
||||
name=package_name,
|
||||
version=version)
|
||||
|
||||
repository = repositories.get(package_name, release_data['home_page'])
|
||||
repo_markup_1, repo_markup_2 = get_repo_markup(repository)
|
||||
|
||||
# first row: name, images and simple links
|
||||
url = '.. image:: {site}/status/{name}-latest'
|
||||
image_url = url.format(**common_params)
|
||||
image_url += '?py={py}&pytest={pytest}'
|
||||
row = (
|
||||
ColumnData(package_name + "-" + version, release_data['package_url']),
|
||||
ColumnData(image_url.format(py='py27', pytest=pytest_version),
|
||||
None),
|
||||
ColumnData(image_url.format(py='py34', pytest=pytest_version),
|
||||
None),
|
||||
ColumnData(
|
||||
repo_markup_1,
|
||||
None),
|
||||
ColumnData(sanitize_summary(release_data['summary']), None),
|
||||
)
|
||||
assert len(row) == len(headers)
|
||||
rows.append(row)
|
||||
|
||||
# second row: links for images (they should be in their own line)
|
||||
url = ' :target: {site}/output/{name}-latest'
|
||||
output_url = url.format(**common_params)
|
||||
output_url += '?py={py}&pytest={pytest}'
|
||||
|
||||
row = (
|
||||
ColumnData('', None),
|
||||
ColumnData(output_url.format(py='py27', pytest=pytest_version),
|
||||
None),
|
||||
ColumnData(output_url.format(py='py34', pytest=pytest_version),
|
||||
None),
|
||||
ColumnData(repo_markup_2, None),
|
||||
ColumnData('', None),
|
||||
|
||||
)
|
||||
assert len(row) == len(headers)
|
||||
rows.append(row)
|
||||
|
||||
print('OK (%d%%)' % ((index + 1) * 100 / len(plugins)))
|
||||
|
||||
return headers, rows
|
||||
|
||||
|
||||
def obtain_override_repositories():
|
||||
"""
|
||||
Used to override the "home_page" obtained from pypi to known
|
||||
package repositories. Used when the author didn't fill the "home_page"
|
||||
field in setup.py.
|
||||
|
||||
:return: dict of {package_name: repository_url}
|
||||
"""
|
||||
return {
|
||||
'pytest-blockage': 'https://github.com/rob-b/pytest-blockage',
|
||||
'pytest-konira': 'http://github.com/alfredodeza/pytest-konira',
|
||||
'pytest-sugar': 'https://github.com/Frozenball/pytest-sugar',
|
||||
}
|
||||
|
||||
|
||||
def generate_plugins_index_from_table(filename, headers, rows):
|
||||
"""
|
||||
Generates a RST file with the table data given.
|
||||
|
||||
:param filename: output filename
|
||||
:param headers: see `obtain_plugins_table`
|
||||
:param rows: see `obtain_plugins_table`
|
||||
"""
|
||||
# creates a list of rows, each being a str containing appropriate column
|
||||
# text and link
|
||||
table_texts = []
|
||||
for row in rows:
|
||||
column_texts = []
|
||||
for i, col_data in enumerate(row):
|
||||
text = '`%s <%s>`_' % (
|
||||
col_data.text,
|
||||
col_data.link) if col_data.link else col_data.text
|
||||
column_texts.append(text)
|
||||
table_texts.append(column_texts)
|
||||
|
||||
# compute max length of each column so we can build the rst table
|
||||
column_lengths = [len(x) for x in headers]
|
||||
for column_texts in table_texts:
|
||||
for i, row_text in enumerate(column_texts):
|
||||
column_lengths[i] = max(column_lengths[i], len(row_text) + 2)
|
||||
|
||||
def get_row_limiter(char):
|
||||
return ' '.join(char * length for length in column_lengths)
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
# header
|
||||
header_text = HEADER.format(pytest_version=pytest.__version__)
|
||||
print(header_text, file=f)
|
||||
print(file=f)
|
||||
|
||||
# table
|
||||
print(get_row_limiter('='), file=f)
|
||||
formatted_headers = [
|
||||
'{0:^{fill}}'.format(header, fill=column_lengths[i])
|
||||
for i, header in enumerate(headers)]
|
||||
print(*formatted_headers, file=f)
|
||||
print(get_row_limiter('='), file=f)
|
||||
|
||||
for column_texts in table_texts:
|
||||
formatted_rows = [
|
||||
'{0:^{fill}}'.format(row_text, fill=column_lengths[i])
|
||||
for i, row_text in enumerate(column_texts)
|
||||
]
|
||||
print(*formatted_rows, file=f)
|
||||
print(file=f)
|
||||
print(get_row_limiter('='), file=f)
|
||||
print(file=f)
|
||||
today = datetime.date.today().strftime('%Y-%m-%d')
|
||||
print('*(Updated on %s)*' % today, file=f)
|
||||
|
||||
|
||||
def generate_plugins_index(client, filename):
|
||||
"""
|
||||
Generates an RST file with a table of the latest pytest plugins found in
|
||||
PyPI.
|
||||
|
||||
:param client: ServerProxy
|
||||
:param filename: output filename
|
||||
"""
|
||||
plugins = get_latest_versions(iter_plugins(client))
|
||||
headers, rows = obtain_plugins_table(plugins, client)
|
||||
generate_plugins_index_from_table(filename, headers, rows)
|
||||
|
||||
|
||||
def main(argv):
|
||||
"""
|
||||
Script entry point. Configures an option parser and calls the appropriate
|
||||
internal function.
|
||||
"""
|
||||
filename = os.path.join(os.path.dirname(__file__), 'index.txt')
|
||||
url = 'http://pypi.python.org/pypi'
|
||||
|
||||
parser = OptionParser(
|
||||
description='Generates a restructured document of pytest plugins from PyPI')
|
||||
parser.add_option('-f', '--filename', default=filename,
|
||||
help='output filename [default: %default]')
|
||||
parser.add_option('-u', '--url', default=url,
|
||||
help='url of PyPI server to obtain data from [default: %default]')
|
||||
(options, _) = parser.parse_args(argv[1:])
|
||||
|
||||
client = get_proxy(options.url)
|
||||
generate_plugins_index(client, options.filename)
|
||||
|
||||
print()
|
||||
print('%s Updated.' % options.filename)
|
||||
return 0
|
||||
|
||||
|
||||
# header for the plugins_index page
|
||||
HEADER = '''.. _plugins_index:
|
||||
|
||||
List of Third-Party Plugins
|
||||
===========================
|
||||
|
||||
The table below contains a listing of plugins found in PyPI and
|
||||
their status when tested using py.test **{pytest_version}** and python 2.7 and
|
||||
3.3.
|
||||
|
||||
A complete listing can also be found at
|
||||
`pytest-plugs <http://pytest-plugs.herokuapp.com/>`_, which contains tests
|
||||
status against other py.test releases.
|
||||
'''
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -20,10 +20,10 @@
|
||||
Project examples
|
||||
==========================
|
||||
|
||||
Here are some examples of projects using py.test (please send notes via :ref:`contact`):
|
||||
Here are some examples of projects using ``pytest`` (please send notes via :ref:`contact`):
|
||||
|
||||
* `PyPy <http://pypy.org>`_, Python with a JIT compiler, running over
|
||||
`16000 tests <http://buildbot.pypy.org/summary?branch=%3Ctrunk%3E>`_
|
||||
* `PyPy <http://pypy.org>`_, Python with a JIT compiler, running over
|
||||
`21000 tests <http://buildbot.pypy.org/summary?branch=%3Ctrunk%3E>`_
|
||||
* the `MoinMoin <http://moinmo.in>`_ Wiki Engine
|
||||
* `sentry <https://getsentry.com/welcome/>`_, realtime app-maintenance and exception tracking
|
||||
* `tox <http://testrun.org/tox>`_, virtualenv/Hudson integration tool
|
||||
@@ -60,7 +60,7 @@ Here are some examples of projects using py.test (please send notes via :ref:`co
|
||||
* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver
|
||||
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
|
||||
|
||||
These projects help integrate py.test into other Python frameworks:
|
||||
These projects help integrate ``pytest`` into other Python frameworks:
|
||||
|
||||
* `pytest-django <http://pypi.python.org/pypi/pytest-django/>`_ for Django
|
||||
* `zope.pytest <http://packages.python.org/zope.pytest/>`_ for Zope and Grok
|
||||
@@ -68,7 +68,7 @@ These projects help integrate py.test into other Python frameworks:
|
||||
* There is `some work <https://github.com/Kotti/Kotti/blob/master/kotti/testing.py>`_ underway for Kotti, a CMS built in Pyramid/Pylons
|
||||
|
||||
|
||||
Some organisations using py.test
|
||||
Some organisations using pytest
|
||||
-----------------------------------
|
||||
|
||||
* `Square Kilometre Array, Cape Town <http://ska.ac.za/>`_
|
||||
|
||||
@@ -14,7 +14,7 @@ A *skip* means that you expect your test to pass unless the environment
|
||||
And *xfail* means that your test can run but you expect it to fail
|
||||
because there is an implementation problem.
|
||||
|
||||
py.test counts and lists *skip* and *xfail* tests separately. Detailed
|
||||
``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
|
||||
information about skipped/xfailed tests is not shown by default to avoid
|
||||
cluttering the output. You can use the ``-r`` option to see details
|
||||
corresponding to the "short" letters shown in the test progress::
|
||||
@@ -29,13 +29,13 @@ corresponding to the "short" letters shown in the test progress::
|
||||
Marking a test function to be skipped
|
||||
-------------------------------------------
|
||||
|
||||
.. versionadded:: 2.0, 2.4
|
||||
.. versionadded:: 2.0, 2.4
|
||||
|
||||
Here is an example of marking a test function to be skipped
|
||||
when run on a Python3.3 interpreter::
|
||||
|
||||
import sys
|
||||
@pytest.mark.skipif(sys.version_info >= (3,3),
|
||||
@pytest.mark.skipif(sys.version_info < (3,3),
|
||||
reason="requires python3.3")
|
||||
def test_function():
|
||||
...
|
||||
@@ -51,7 +51,7 @@ You can share skipif markers between modules. Consider this test module::
|
||||
# content of test_mymodule.py
|
||||
|
||||
import mymodule
|
||||
minversion = pytest.mark.skipif(mymodule.__versioninfo__ >= (1,1),
|
||||
minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1),
|
||||
reason="at least mymodule-1.1 required")
|
||||
@minversion
|
||||
def test_function():
|
||||
@@ -70,7 +70,8 @@ For larger test suites it's usually a good idea to have one file
|
||||
where you define the markers which you then consistently apply
|
||||
throughout your test suite.
|
||||
|
||||
Alternatively, the pre pytest-2.4 way to specify `condition strings <condition strings>`_ instead of booleans will remain fully supported in future
|
||||
Alternatively, the pre pytest-2.4 way to specify :ref:`condition strings
|
||||
<string conditions>` instead of booleans will remain fully supported in future
|
||||
versions of pytest. It couldn't be easily used for importing markers
|
||||
between test modules so it's no longer advertised as the primary method.
|
||||
|
||||
@@ -103,7 +104,7 @@ you can set the ``pytestmark`` attribute of a class::
|
||||
"will not be setup or run under 'win32' platform"
|
||||
|
||||
As with the class-decorator, the ``pytestmark`` special name tells
|
||||
py.test to apply it to each test function in the class.
|
||||
``pytest`` to apply it to each test function in the class.
|
||||
|
||||
If you want to skip all test functions of a module, you must use
|
||||
the ``pytestmark`` name on the global level::
|
||||
@@ -158,7 +159,7 @@ Running it with the report-on-xfail option gives this output::
|
||||
|
||||
example $ py.test -rx xfail_demo.py
|
||||
=========================== test session starts ============================
|
||||
platform linux2 -- Python 2.7.3 -- pytest-2.4.2
|
||||
platform linux2 -- Python 2.7.6 -- py-1.4.22 -- pytest-2.6.0
|
||||
collected 6 items
|
||||
|
||||
xfail_demo.py xxxxxx
|
||||
@@ -211,7 +212,7 @@ imperatively, in test or setup code::
|
||||
if not valid_config():
|
||||
pytest.xfail("failing configuration (but should work)")
|
||||
# or
|
||||
pytest.skipif("unsupported configuration")
|
||||
pytest.skip("unsupported configuration")
|
||||
|
||||
|
||||
Skipping on a missing import dependency
|
||||
@@ -232,7 +233,7 @@ The version will be read from the specified
|
||||
module's ``__version__`` attribute.
|
||||
|
||||
|
||||
.. _`string conditions`:
|
||||
.. _string conditions:
|
||||
|
||||
specifying conditions as strings versus booleans
|
||||
----------------------------------------------------------
|
||||
@@ -254,7 +255,7 @@ because markers can then be freely imported between test modules.
|
||||
With strings you need to import not only the marker but all variables
|
||||
everything used by the marker, which violates encapsulation.
|
||||
|
||||
The reason for specifying the condition as a string was that py.test can
|
||||
The reason for specifying the condition as a string was that ``pytest`` can
|
||||
report a summary of skip conditions based purely on the condition string.
|
||||
With conditions as booleans you are required to specify a ``reason`` string.
|
||||
|
||||
@@ -285,4 +286,9 @@ The equivalent with "boolean conditions" is::
|
||||
def test_function(...):
|
||||
pass
|
||||
|
||||
.. note::
|
||||
|
||||
You cannot use ``pytest.config.getvalue()`` in code
|
||||
imported before py.test's argument parsing takes place. For example,
|
||||
``conftest.py`` files are imported before command line parsing and thus
|
||||
``config.getvalue()`` will not execute correctly.
|
||||
|
||||
5
doc/en/status.txt
Normal file
5
doc/en/status.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
pytest development status
|
||||
================================
|
||||
|
||||
https://drone.io/bitbucket.org/hpk42/pytest
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user