Compare commits
642 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f838c7b7eb | ||
|
|
25b53c4196 | ||
|
|
fc27171d57 | ||
|
|
d18e426b24 | ||
|
|
e83fa48dd1 | ||
|
|
c53d52c3f2 | ||
|
|
3886c6d735 | ||
|
|
80936b6762 | ||
|
|
5ca08e9b8a | ||
|
|
ba2c49e71e | ||
|
|
da615a4fbe | ||
|
|
32562881d4 | ||
|
|
2a0bbfe63f | ||
|
|
9aed656ec7 | ||
|
|
a600e7a2a4 | ||
|
|
57a95b3a2c | ||
|
|
f5e430fd8f | ||
|
|
38a4c7e56c | ||
|
|
40f02d72b0 | ||
|
|
554f600fb4 | ||
|
|
e3a3c90d94 | ||
|
|
f7327759e8 | ||
|
|
ee1950af77 | ||
|
|
3d0f3baa2b | ||
|
|
8da758b93a | ||
|
|
59e5d1bfbf | ||
|
|
b9e2cd0a81 | ||
|
|
a84fcbf5b2 | ||
|
|
59c1bfada7 | ||
|
|
3267f64724 | ||
|
|
c9fd1bdbd6 | ||
|
|
93aa988e01 | ||
|
|
7996724f23 | ||
|
|
90ee8a7599 | ||
|
|
378a75ddf6 | ||
|
|
e1b3a68462 | ||
|
|
fb7dbc9fa3 | ||
|
|
a0ea300e96 | ||
|
|
09b289e286 | ||
|
|
694dbe5bd4 | ||
|
|
4f8fff9cab | ||
|
|
ac7ebfa22e | ||
|
|
db92cea14c | ||
|
|
bce1d40fb0 | ||
|
|
dc86fb6758 | ||
|
|
2df4f63149 | ||
|
|
e3cf4fc258 | ||
|
|
978b315861 | ||
|
|
580edc13e7 | ||
|
|
1df593f978 | ||
|
|
b7f2e3d4f5 | ||
|
|
f011bc642c | ||
|
|
b1d7a187f2 | ||
|
|
678d65f051 | ||
|
|
fcd3fad03d | ||
|
|
9e8540f25f | ||
|
|
19bb2c6235 | ||
|
|
bc8e52c3c2 | ||
|
|
333bb0883a | ||
|
|
dce2621710 | ||
|
|
acec0b688f | ||
|
|
197b7c3bce | ||
|
|
37d074efc8 | ||
|
|
a5a8d53dfe | ||
|
|
9fd71d6fe0 | ||
|
|
b11bfa106c | ||
|
|
bd7e33277b | ||
|
|
ddc8edffbc | ||
|
|
bdd22fdd52 | ||
|
|
194b52145b | ||
|
|
15e1dd0f87 | ||
|
|
23c43a37e0 | ||
|
|
92767fec51 | ||
|
|
5fc80d8bc3 | ||
|
|
2f60548e08 | ||
|
|
f10ab021e2 | ||
|
|
ff7b5dbbde | ||
|
|
4b53bbc0a9 | ||
|
|
769ffc32bf | ||
|
|
5819536f00 | ||
|
|
952cab2d85 | ||
|
|
d636fcd557 | ||
|
|
f77d606d4e | ||
|
|
16c683dff9 | ||
|
|
2e48c32dea | ||
|
|
2451716746 | ||
|
|
6a7df7f031 | ||
|
|
ac3a42bafd | ||
|
|
be23aeb989 | ||
|
|
bfd0d18371 | ||
|
|
9928c7794b | ||
|
|
706ea86bba | ||
|
|
1d5a0ef284 | ||
|
|
c8b4a1a471 | ||
|
|
68fe0eb8f3 | ||
|
|
de854c6ee1 | ||
|
|
04126feea7 | ||
|
|
eeebcd77dd | ||
|
|
478a244f5e | ||
|
|
47ccd58fb4 | ||
|
|
2277817176 | ||
|
|
1baeefc2fd | ||
|
|
260f848c05 | ||
|
|
7c0d1cad40 | ||
|
|
b26e60c2da | ||
|
|
2be06ba67e | ||
|
|
8e991a622c | ||
|
|
b099fcfa33 | ||
|
|
4c9b850e13 | ||
|
|
81a9df6ed1 | ||
|
|
58ef95ed4d | ||
|
|
435ad221f9 | ||
|
|
d1b50526fa | ||
|
|
4d633a29be | ||
|
|
8a1633c3b4 | ||
|
|
82f5986424 | ||
|
|
fb16d3e27a | ||
|
|
2b13a9b95d | ||
|
|
1b30514783 | ||
|
|
af2b0e1174 | ||
|
|
781a730bea | ||
|
|
7c09d88b72 | ||
|
|
4021770688 | ||
|
|
f95c7f5803 | ||
|
|
24dcc76495 | ||
|
|
442f7a7706 | ||
|
|
d18c75baa3 | ||
|
|
bc976dca3b | ||
|
|
7b8968ff80 | ||
|
|
0c68e7a2c9 | ||
|
|
369284752e | ||
|
|
e872532d0c | ||
|
|
9785ee438d | ||
|
|
959e6b4f44 | ||
|
|
5945c3fe88 | ||
|
|
7155b2277c | ||
|
|
a7a1686433 | ||
|
|
371939fb86 | ||
|
|
7fc9d4c976 | ||
|
|
2b5adc88a7 | ||
|
|
02aa8adae1 | ||
|
|
67e69a7e49 | ||
|
|
b09762df27 | ||
|
|
9631b3c166 | ||
|
|
4b70ba2c21 | ||
|
|
de3353aac1 | ||
|
|
ef73a56032 | ||
|
|
d839686c7b | ||
|
|
d89b5057ca | ||
|
|
83137c89e9 | ||
|
|
dbae5a7ff8 | ||
|
|
07b7b6fa7d | ||
|
|
56a5dbe252 | ||
|
|
8a4d5227e2 | ||
|
|
e6ea9edffe | ||
|
|
b7ad4c2bed | ||
|
|
d79179a239 | ||
|
|
c9f9664336 | ||
|
|
aa4d80cad9 | ||
|
|
4209ad6fca | ||
|
|
3ea74310d7 | ||
|
|
c3e53a072d | ||
|
|
fa877665ad | ||
|
|
3b582858f3 | ||
|
|
78baa7b575 | ||
|
|
7484e346f9 | ||
|
|
d25123eb01 | ||
|
|
d33da078a8 | ||
|
|
d59adc61f9 | ||
|
|
7cc513b2af | ||
|
|
0ee007ca33 | ||
|
|
1ebca37689 | ||
|
|
b59d32a5c7 | ||
|
|
3e4e6297ce | ||
|
|
0b2b40e35d | ||
|
|
f9dd58000a | ||
|
|
449290406c | ||
|
|
12824e6279 | ||
|
|
a62d9a40e7 | ||
|
|
30cb598e9c | ||
|
|
114de91ab7 | ||
|
|
7a7c634e33 | ||
|
|
b4ace46c42 | ||
|
|
a8fc056aad | ||
|
|
9e262038c8 | ||
|
|
ef437ea448 | ||
|
|
cdc7e13067 | ||
|
|
39d9f7cff5 | ||
|
|
632800add5 | ||
|
|
bc494661ad | ||
|
|
5a4c1b628b | ||
|
|
4316fe8a92 | ||
|
|
aa0328782f | ||
|
|
9c7f1d9b32 | ||
|
|
dab90ef726 | ||
|
|
61f2a26675 | ||
|
|
b0d45267c5 | ||
|
|
1480aa31a7 | ||
|
|
75714ee707 | ||
|
|
abffd16ce6 | ||
|
|
fb289667e3 | ||
|
|
8ec4d03c91 | ||
|
|
c55bf23cbe | ||
|
|
99d162e44a | ||
|
|
8bd612b367 | ||
|
|
5256542ea4 | ||
|
|
50f81db817 | ||
|
|
2681b0aed7 | ||
|
|
b10ab0211c | ||
|
|
09a0e45492 | ||
|
|
a9c5d31806 | ||
|
|
c9eeafade5 | ||
|
|
39b25ddcf3 | ||
|
|
4038d6c773 | ||
|
|
a435faad5c | ||
|
|
3645ba3072 | ||
|
|
70739296e1 | ||
|
|
8301993e5e | ||
|
|
1dc265e34a | ||
|
|
e7a833635d | ||
|
|
fed5356941 | ||
|
|
5b81bd862c | ||
|
|
2902c7263c | ||
|
|
442dccef65 | ||
|
|
e830432592 | ||
|
|
ae788997f2 | ||
|
|
88b800355a | ||
|
|
864338de71 | ||
|
|
eb5e651d7e | ||
|
|
55bffb7c15 | ||
|
|
be18f85a6e | ||
|
|
28b8f3ca3a | ||
|
|
d91459fc75 | ||
|
|
4de8e680e3 | ||
|
|
ef283efc42 | ||
|
|
b5b6e051ed | ||
|
|
10b1b79f4e | ||
|
|
6d7e06e6be | ||
|
|
78eddcb5b1 | ||
|
|
757873edb3 | ||
|
|
64ab68ff0a | ||
|
|
66330444a3 | ||
|
|
97f1645993 | ||
|
|
3dbc61dd80 | ||
|
|
b42938421e | ||
|
|
87fecce77b | ||
|
|
05d953d9e4 | ||
|
|
d478e2bbca | ||
|
|
595d62bc3e | ||
|
|
8c41236c66 | ||
|
|
99b90f45d0 | ||
|
|
d848a20563 | ||
|
|
06a7fef00f | ||
|
|
55e5817570 | ||
|
|
3f4b8d3aec | ||
|
|
a3f482ceba | ||
|
|
3ccf2a5e61 | ||
|
|
8e1d59a8dd | ||
|
|
7eaf98af4b | ||
|
|
d282424589 | ||
|
|
ddaa5d88ac | ||
|
|
4ff90b1fcf | ||
|
|
2d29c3e7d1 | ||
|
|
abd5fc80e8 | ||
|
|
80d4dd6f0b | ||
|
|
e440b43258 | ||
|
|
e25d46aae6 | ||
|
|
7c87874277 | ||
|
|
1cf9e68dbc | ||
|
|
b2e6f66438 | ||
|
|
b01e379428 | ||
|
|
1586653102 | ||
|
|
18ac7e0b79 | ||
|
|
c854daa234 | ||
|
|
12c5a6af64 | ||
|
|
30922ee694 | ||
|
|
35ba053f00 | ||
|
|
d0cb16010b | ||
|
|
ad0f4f0ac0 | ||
|
|
9c716e4d74 | ||
|
|
94ac0f7e6b | ||
|
|
440881d63a | ||
|
|
aa318e9adf | ||
|
|
20b66e60c0 | ||
|
|
d017b69f38 | ||
|
|
ae5d16be10 | ||
|
|
cbad319736 | ||
|
|
a9eab07739 | ||
|
|
c2980eb80f | ||
|
|
bf5c76359c | ||
|
|
40758e86ca | ||
|
|
d678d380cb | ||
|
|
3f8f395210 | ||
|
|
7c52a37d46 | ||
|
|
198b1dcffd | ||
|
|
e2934c3f8c | ||
|
|
6b7e1a246c | ||
|
|
817c094ce6 | ||
|
|
df1f43ee28 | ||
|
|
778d4364fa | ||
|
|
039d582b52 | ||
|
|
9b8039cf09 | ||
|
|
b687f20d25 | ||
|
|
510be29db8 | ||
|
|
fe343a79f8 | ||
|
|
1e3bc1814d | ||
|
|
57512aa997 | ||
|
|
2e8f7ef31b | ||
|
|
c51173d426 | ||
|
|
2da331ea9c | ||
|
|
94d8c071b6 | ||
|
|
0d55fb3797 | ||
|
|
f3967333a1 | ||
|
|
a76bc64c54 | ||
|
|
dd5c2b22bd | ||
|
|
a29d6194f5 | ||
|
|
6f2943c7b3 | ||
|
|
5e15c86cc6 | ||
|
|
498884a2a0 | ||
|
|
09bdbffbde | ||
|
|
09ab5fd7e9 | ||
|
|
040a61e22c | ||
|
|
2c32dad343 | ||
|
|
8a8f9bd751 | ||
|
|
192d3adda3 | ||
|
|
5865520c51 | ||
|
|
e2fa78c99f | ||
|
|
fe895a40b6 | ||
|
|
934f38995a | ||
|
|
5dcd24fecb | ||
|
|
e5a362d0f5 | ||
|
|
f8654e6656 | ||
|
|
0d4f479aa8 | ||
|
|
79d00ab35a | ||
|
|
79ae86cc3f | ||
|
|
c051a9e7b9 | ||
|
|
cdd6f86e43 | ||
|
|
83451b548f | ||
|
|
38538c6c6d | ||
|
|
55ebd9f803 | ||
|
|
8ca8d25202 | ||
|
|
a8d67f5e7b | ||
|
|
03bc8aba4e | ||
|
|
6b13379f37 | ||
|
|
9dcdea5de7 | ||
|
|
863bab5326 | ||
|
|
8521503246 | ||
|
|
0b6258ab5b | ||
|
|
b63cb18776 | ||
|
|
00097df5cd | ||
|
|
10e243d206 | ||
|
|
e7444bbd5e | ||
|
|
eb7a57f965 | ||
|
|
252eae5bc8 | ||
|
|
9c7b3c57d7 | ||
|
|
1350c601dc | ||
|
|
ef112fd7dd | ||
|
|
8fa57c8384 | ||
|
|
ad02f6f879 | ||
|
|
bd6ba3f3e1 | ||
|
|
e17f5fad14 | ||
|
|
72d06e6dec | ||
|
|
aca1723d45 | ||
|
|
85df6bbe26 | ||
|
|
ba49581510 | ||
|
|
0bf9628e62 | ||
|
|
d878d9d4d5 | ||
|
|
93b74d28d2 | ||
|
|
8c4dfca0c1 | ||
|
|
b8787b8732 | ||
|
|
ef294fc727 | ||
|
|
0f78ef8e02 | ||
|
|
1cd4bafda7 | ||
|
|
8f5fd537d8 | ||
|
|
9869a3d9e1 | ||
|
|
2f00b041e4 | ||
|
|
62db3f7abc | ||
|
|
7ee27fedee | ||
|
|
7b1e3d1c9a | ||
|
|
a52f791461 | ||
|
|
fb99b5c66e | ||
|
|
ddfa41b5a7 | ||
|
|
6a26ac4125 | ||
|
|
cdaa9c06e1 | ||
|
|
0dc82e8501 | ||
|
|
f9bed82c4d | ||
|
|
2406076611 | ||
|
|
44eb1f580c | ||
|
|
3392be37e1 | ||
|
|
3d2680b31b | ||
|
|
0b603156b9 | ||
|
|
0c247be769 | ||
|
|
3e6f0f34ff | ||
|
|
4fb9cc3bf0 | ||
|
|
930a158a6a | ||
|
|
d347a30656 | ||
|
|
32b62f770f | ||
|
|
aaae43e0ba | ||
|
|
1a75a3c08e | ||
|
|
09e9a01df3 | ||
|
|
4b974b051d | ||
|
|
5c445b05e7 | ||
|
|
4e0dbe92dd | ||
|
|
956389fa8c | ||
|
|
61f985f3c7 | ||
|
|
2f0d0fb349 | ||
|
|
e98176cf50 | ||
|
|
38fc208205 | ||
|
|
89f92a459a | ||
|
|
40d0031cce | ||
|
|
d0811c1f3d | ||
|
|
ac41f36a02 | ||
|
|
7a626921c0 | ||
|
|
59bc6efbf2 | ||
|
|
a7292a0544 | ||
|
|
1971033051 | ||
|
|
d4d04e7f25 | ||
|
|
4f0eec2022 | ||
|
|
cc7f294cfe | ||
|
|
e1298faef9 | ||
|
|
e211d6fe2a | ||
|
|
544b4a14d5 | ||
|
|
4a42deee7e | ||
|
|
2cce026766 | ||
|
|
e16cb2fdd0 | ||
|
|
dcb94d8f31 | ||
|
|
19f66cb824 | ||
|
|
36944157f8 | ||
|
|
2cfee583db | ||
|
|
56dc301c50 | ||
|
|
aa05334984 | ||
|
|
4806878a7f | ||
|
|
d1d7e5d41b | ||
|
|
1667cf3350 | ||
|
|
5b0e255e85 | ||
|
|
f0fdafeddc | ||
|
|
a4f5b8a4d6 | ||
|
|
5049e25a6a | ||
|
|
d36c712bb0 | ||
|
|
7a0d1b387d | ||
|
|
5f4cd536f9 | ||
|
|
83813bf515 | ||
|
|
749752d440 | ||
|
|
118cb3d3be | ||
|
|
b91c721262 | ||
|
|
4630e2725e | ||
|
|
ea31649062 | ||
|
|
3789bb53a7 | ||
|
|
9298f7e4a9 | ||
|
|
715f56dfbc | ||
|
|
ab6406b42e | ||
|
|
99180939fe | ||
|
|
1ec5befdb7 | ||
|
|
8ba0b7bc2a | ||
|
|
1d3f27cef0 | ||
|
|
29703a5f51 | ||
|
|
6f7a95c32e | ||
|
|
bebfd28da3 | ||
|
|
f5844449a8 | ||
|
|
f2659f77be | ||
|
|
b9c136b809 | ||
|
|
fd1691a2b3 | ||
|
|
4a265ba38b | ||
|
|
910d5df6a8 | ||
|
|
21d189eb52 | ||
|
|
e9d9f71374 | ||
|
|
00adb4e42f | ||
|
|
b2cb87fae6 | ||
|
|
189fe3ba1d | ||
|
|
d291905825 | ||
|
|
f0c7f21312 | ||
|
|
4ff7453b48 | ||
|
|
0e70acab79 | ||
|
|
8eec42f040 | ||
|
|
3adaa3d87b | ||
|
|
090e260517 | ||
|
|
117072d64c | ||
|
|
90740007a8 | ||
|
|
5e1c6ce630 | ||
|
|
a136111dcc | ||
|
|
61d04d3084 | ||
|
|
a3bc6df950 | ||
|
|
2d488f7615 | ||
|
|
1356d20e90 | ||
|
|
622995a501 | ||
|
|
1e28cb855d | ||
|
|
36531599a4 | ||
|
|
29db2da9a7 | ||
|
|
24f8002de8 | ||
|
|
5b295ec68e | ||
|
|
fa645a7003 | ||
|
|
13baab746d | ||
|
|
9f1ade9acf | ||
|
|
23475b6ab9 | ||
|
|
2d2c67d7c0 | ||
|
|
cff7843f3b | ||
|
|
356d865ad7 | ||
|
|
f46ad8d114 | ||
|
|
8dbf6a4b2d | ||
|
|
0e00069340 | ||
|
|
e8c8559efa | ||
|
|
91a96ec3d6 | ||
|
|
12f74a28fa | ||
|
|
0fa35960ba | ||
|
|
c627ac4e59 | ||
|
|
26a2e1aba7 | ||
|
|
7c80335c6b | ||
|
|
75f964c08d | ||
|
|
4fa819e535 | ||
|
|
61061d2147 | ||
|
|
deb4287d1c | ||
|
|
7aac48c418 | ||
|
|
0375c1b728 | ||
|
|
4848bbdf9a | ||
|
|
8656c1a61d | ||
|
|
3b60e36dbb | ||
|
|
162d737f68 | ||
|
|
d884164160 | ||
|
|
9811ebdc57 | ||
|
|
1c0242dec1 | ||
|
|
a5224f7490 | ||
|
|
a5863ca760 | ||
|
|
8077168387 | ||
|
|
afa899d5e1 | ||
|
|
8de9b1be56 | ||
|
|
b532d15fe7 | ||
|
|
73702ca88b | ||
|
|
d7b0389d1a | ||
|
|
06206bcf37 | ||
|
|
466bbbf8e8 | ||
|
|
a9608d54e0 | ||
|
|
9924432a2e | ||
|
|
c7ac3379e7 | ||
|
|
6cd61390c2 | ||
|
|
994909270f | ||
|
|
e1df9dbf0d | ||
|
|
ab44d3d733 | ||
|
|
d76aa8b428 | ||
|
|
4dfd2947bc | ||
|
|
ed57b8e08a | ||
|
|
536177bb56 | ||
|
|
dc7bf518b3 | ||
|
|
75493f78bf | ||
|
|
c487cf9dd5 | ||
|
|
8be9684ab2 | ||
|
|
d4879c7afb | ||
|
|
afbaee7649 | ||
|
|
d42f5a41a5 | ||
|
|
853889e5db | ||
|
|
a176ff77bc | ||
|
|
7f24cc2feb | ||
|
|
d0c663a628 | ||
|
|
f7409f8685 | ||
|
|
10fcac7f90 | ||
|
|
8942a05cfe | ||
|
|
ecd1e43afb | ||
|
|
66c1a120ba | ||
|
|
226f0c48bf | ||
|
|
9b74bf1e0c | ||
|
|
fa51a26743 | ||
|
|
59067ad33d | ||
|
|
cbb2f9541b | ||
|
|
b29ae03cb6 | ||
|
|
0711060422 | ||
|
|
1ef29ab548 | ||
|
|
e13ad22364 | ||
|
|
8cdf9d43a9 | ||
|
|
2ddc330b62 | ||
|
|
30f2729684 | ||
|
|
3812985ed4 | ||
|
|
c6ed69a666 | ||
|
|
3a0f436c1a | ||
|
|
1c0ab3c2a3 | ||
|
|
7ff91d8127 | ||
|
|
64c71daa21 | ||
|
|
a326fa22c6 | ||
|
|
623b3982b0 | ||
|
|
5a7de2c2cb | ||
|
|
24d4882d82 | ||
|
|
ac5929eef3 | ||
|
|
e24b6b0388 | ||
|
|
c7f9fda42d | ||
|
|
985ac09048 | ||
|
|
1c4a672a52 | ||
|
|
59f95b7f59 | ||
|
|
41b7b109e9 | ||
|
|
42fb1f7ede | ||
|
|
c9ec724886 | ||
|
|
c00a43a17d | ||
|
|
1dc612f7d4 | ||
|
|
e5bd7fb053 | ||
|
|
256a9e0027 | ||
|
|
16ff9f591e | ||
|
|
d2d7b97a70 | ||
|
|
23f6adc760 | ||
|
|
172b82875a | ||
|
|
277857b026 | ||
|
|
d3ab56f531 | ||
|
|
da091b832d | ||
|
|
8feeb09398 | ||
|
|
209d99102d | ||
|
|
6d31684da5 | ||
|
|
4ee280ae1f | ||
|
|
8b8cba369c | ||
|
|
b6b0bc03f3 | ||
|
|
a1219ab8fc | ||
|
|
83182b82ea | ||
|
|
31d5cedc6d | ||
|
|
91b3ff1bb7 | ||
|
|
b66dc80008 | ||
|
|
69ad2026f6 | ||
|
|
47ae1fb36b | ||
|
|
bc7282576f | ||
|
|
d7d5cf4136 | ||
|
|
2d24c062b6 | ||
|
|
d940d0b657 | ||
|
|
f9f092a5e6 | ||
|
|
a3c8246b60 | ||
|
|
15ffe63204 | ||
|
|
c99c7d0f95 | ||
|
|
886b8d27c6 | ||
|
|
a4408eb9c1 | ||
|
|
b0ebcfb785 | ||
|
|
a02310a140 | ||
|
|
2fa0518e89 | ||
|
|
df0c652333 | ||
|
|
5820c5c5a6 | ||
|
|
ed012c808a | ||
|
|
6b75a7733b | ||
|
|
2d449e95e4 | ||
|
|
dac16cd9e5 | ||
|
|
2c941b5d13 | ||
|
|
98c899c9b0 | ||
|
|
f1224a0e85 | ||
|
|
ccb3ef3b33 | ||
|
|
c0b1a39192 | ||
|
|
51f9cd0e02 | ||
|
|
faea273c93 | ||
|
|
426a4cdca9 | ||
|
|
59369651db | ||
|
|
f7b1de70c0 | ||
|
|
04f27d4eb4 |
@@ -24,3 +24,6 @@ exclude_lines =
|
||||
\#\s*pragma: no cover
|
||||
^\s*raise NotImplementedError\b
|
||||
^\s*return NotImplemented\b
|
||||
^\s*assert False(,|$)
|
||||
|
||||
^\s*if TYPE_CHECKING:
|
||||
|
||||
28
.gitblameignore
Normal file
28
.gitblameignore
Normal file
@@ -0,0 +1,28 @@
|
||||
# List of revisions that can be ignored with git-blame(1).
|
||||
#
|
||||
# See `blame.ignoreRevsFile` in git-config(1) to enable it by default, or
|
||||
# use it with `--ignore-revs-file` manually with git-blame.
|
||||
#
|
||||
# To "install" it:
|
||||
#
|
||||
# git config --local blame.ignoreRevsFile .gitblameignore
|
||||
|
||||
# run black
|
||||
703e4b11ba76171eccd3f13e723c47b810ded7ef
|
||||
# switched to src layout
|
||||
eaa882f3d5340956beb176aa1753e07e3f3f2190
|
||||
# pre-commit run pyupgrade --all-files
|
||||
a91fe1feddbded535a4322ab854429e3a3961fb4
|
||||
# move node base classes from main to nodes
|
||||
afc607cfd81458d4e4f3b1f3cf8cc931b933907e
|
||||
# [?] split most fixture related code into own plugin
|
||||
8c49561470708761f7321504f5e8343811be87ac
|
||||
# run pyupgrade
|
||||
9aacb4635e81edd6ecf281d4f6c0cfc8e94ab301
|
||||
# run blacken-docs
|
||||
5f95dce95602921a70bfbc7d8de2f7712c5e4505
|
||||
# ran pyupgrade-docs again
|
||||
75d0b899bbb56d6849e9d69d83a9426ed3f43f8b
|
||||
|
||||
# move argument parser to own file
|
||||
c9df77cbd6a365dcb73c39618e4842711817e871
|
||||
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -3,10 +3,9 @@ Thanks for submitting a PR, your contribution is really appreciated!
|
||||
|
||||
Here is a quick checklist that should be present in PRs.
|
||||
|
||||
- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes.
|
||||
- [ ] Target the `features` branch for new features, improvements, and removals/deprecations.
|
||||
- [ ] Include documentation when adding new features.
|
||||
- [ ] Include new tests or update existing tests when applicable.
|
||||
- [X] Allow maintainers to push and squash when merging my commits. Please uncheck this if you prefer to squash the commits yourself.
|
||||
|
||||
Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:
|
||||
|
||||
|
||||
205
.github/workflows/main.yml
vendored
Normal file
205
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
# evaluating GitHub actions for CI, disregard failures when evaluating PRs
|
||||
#
|
||||
# this is still missing:
|
||||
# - deploy
|
||||
# - upload github notes
|
||||
#
|
||||
name: main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "[0-9]+.[0-9]+.x"
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- "[0-9]+.[0-9]+.x"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [
|
||||
"windows-py35",
|
||||
"windows-py36",
|
||||
"windows-py37",
|
||||
"windows-py37-pluggy",
|
||||
"windows-py38",
|
||||
|
||||
"ubuntu-py35",
|
||||
"ubuntu-py36",
|
||||
"ubuntu-py37",
|
||||
"ubuntu-py37-pluggy",
|
||||
"ubuntu-py37-freeze",
|
||||
"ubuntu-py38",
|
||||
"ubuntu-py39",
|
||||
"ubuntu-pypy3",
|
||||
|
||||
"macos-py37",
|
||||
"macos-py38",
|
||||
|
||||
"linting",
|
||||
"docs",
|
||||
"doctesting",
|
||||
]
|
||||
|
||||
include:
|
||||
- name: "windows-py35"
|
||||
python: "3.5"
|
||||
os: windows-latest
|
||||
tox_env: "py35-xdist"
|
||||
use_coverage: true
|
||||
- name: "windows-py36"
|
||||
python: "3.6"
|
||||
os: windows-latest
|
||||
tox_env: "py36-xdist"
|
||||
- name: "windows-py37"
|
||||
python: "3.7"
|
||||
os: windows-latest
|
||||
tox_env: "py37-numpy"
|
||||
- name: "windows-py37-pluggy"
|
||||
python: "3.7"
|
||||
os: windows-latest
|
||||
tox_env: "py37-pluggymaster-xdist"
|
||||
- name: "windows-py38"
|
||||
python: "3.8"
|
||||
os: windows-latest
|
||||
tox_env: "py38-unittestextras"
|
||||
use_coverage: true
|
||||
|
||||
- name: "ubuntu-py35"
|
||||
python: "3.5"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py35-xdist"
|
||||
- name: "ubuntu-py36"
|
||||
python: "3.6"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py36-xdist"
|
||||
- name: "ubuntu-py37"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-lsof-numpy-oldattrs-pexpect"
|
||||
use_coverage: true
|
||||
- name: "ubuntu-py37-pluggy"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-pluggymaster-xdist"
|
||||
- name: "ubuntu-py37-freeze"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-freeze"
|
||||
- name: "ubuntu-py38"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py38-xdist"
|
||||
- name: "ubuntu-py39"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py39-xdist"
|
||||
- name: "ubuntu-pypy3"
|
||||
python: "pypy3"
|
||||
os: ubuntu-latest
|
||||
tox_env: "pypy3-xdist"
|
||||
|
||||
- name: "macos-py37"
|
||||
python: "3.7"
|
||||
os: macos-latest
|
||||
tox_env: "py37-xdist"
|
||||
- name: "macos-py38"
|
||||
python: "3.8"
|
||||
os: macos-latest
|
||||
tox_env: "py38-xdist"
|
||||
use_coverage: true
|
||||
|
||||
- name: "linting"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "linting"
|
||||
- name: "docs"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "docs"
|
||||
- name: "doctesting"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "doctesting"
|
||||
use_coverage: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: install python3.9
|
||||
if: matrix.tox_env == 'py39-xdist'
|
||||
run: |
|
||||
sudo add-apt-repository ppa:deadsnakes/nightly
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends python3.9-dev python3.9-distutils
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install tox coverage
|
||||
|
||||
- name: Test without coverage
|
||||
if: "! matrix.use_coverage"
|
||||
run: "tox -e ${{ matrix.tox_env }}"
|
||||
|
||||
- name: Test with coverage
|
||||
if: "matrix.use_coverage"
|
||||
env:
|
||||
_PYTEST_TOX_COVERAGE_RUN: "coverage run -m"
|
||||
COVERAGE_PROCESS_START: ".coveragerc"
|
||||
_PYTEST_TOX_EXTRA_DEP: "coverage-enable-subprocess"
|
||||
run: "tox -e ${{ matrix.tox_env }}"
|
||||
|
||||
- name: Prepare coverage token
|
||||
if: (matrix.use_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' ))
|
||||
run: |
|
||||
python scripts/append_codecov_token.py
|
||||
|
||||
- name: Report coverage
|
||||
if: (matrix.use_coverage)
|
||||
env:
|
||||
CODECOV_NAME: ${{ matrix.name }}
|
||||
run: bash scripts/report-coverage.sh -F GHA,${{ runner.os }}
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: [build]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.7"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade wheel setuptools tox
|
||||
- name: Build package
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
- name: Publish package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.pypi_token }}
|
||||
- name: Publish GitHub release notes
|
||||
env:
|
||||
GH_RELEASE_NOTES_TOKEN: ${{ secrets.release_notes }}
|
||||
run: |
|
||||
sudo apt-get install pandoc
|
||||
tox -e publish-gh-release-notes
|
||||
28
.github/workflows/release-on-comment.yml
vendored
Normal file
28
.github/workflows/release-on-comment.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# part of our release process, see `release-on-comment.py`
|
||||
name: release on comment
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: (github.event.comment && startsWith(github.event.comment.body, '@pytestbot please')) || (github.event.issue && !github.event.comment && startsWith(github.event.issue.body, '@pytestbot please'))
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.8"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade setuptools tox
|
||||
- name: Prepare release
|
||||
run: |
|
||||
tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ secrets.chatops }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,6 +25,7 @@ src/_pytest/_version.py
|
||||
|
||||
doc/*/_build
|
||||
doc/*/.doctrees
|
||||
doc/*/_changelog_towncrier_draft.rst
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
exclude: doc/en/example/py2py3/test_py2.py
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.10b0
|
||||
@@ -9,7 +8,7 @@ repos:
|
||||
rev: v1.0.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies: [black==19.3b0]
|
||||
additional_dependencies: [black==19.10b0]
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.2.3
|
||||
hooks:
|
||||
@@ -38,7 +37,7 @@ repos:
|
||||
- id: pyupgrade
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.740
|
||||
rev: v0.761 # NOTE: keep this in sync with setup.py.
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: ^(src/|testing/)
|
||||
@@ -48,14 +47,14 @@ repos:
|
||||
- id: rst
|
||||
name: rst
|
||||
entry: rst-lint --encoding utf-8
|
||||
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|TIDELIFT.rst|changelog/.*)$
|
||||
files: ^(RELEASING.rst|README.rst|TIDELIFT.rst)$
|
||||
language: python
|
||||
additional_dependencies: [pygments, restructuredtext_lint]
|
||||
- id: changelogs-rst
|
||||
name: changelog filenames
|
||||
language: fail
|
||||
entry: 'changelog files must be named ####.(feature|bugfix|doc|deprecation|removal|vendor|trivial).rst'
|
||||
exclude: changelog/(\d+\.(feature|improvement|bugfix|doc|deprecation|removal|vendor|trivial).rst|README.rst|_template.rst)
|
||||
entry: 'changelog files must be named ####.(breaking|bugfix|deprecation|doc|feature|improvement|trivial|vendor).rst'
|
||||
exclude: changelog/(\d+\.(breaking|bugfix|deprecation|doc|feature|improvement|trivial|vendor).rst|README.rst|_template.rst)
|
||||
files: ^changelog/
|
||||
- id: py-deprecated
|
||||
name: py library is deprecated
|
||||
@@ -65,7 +64,7 @@ repos:
|
||||
_code\.|
|
||||
builtin\.|
|
||||
code\.|
|
||||
io\.(BytesIO|saferepr)|
|
||||
io\.(BytesIO|saferepr|TerminalWriter)|
|
||||
path\.local\.sysfind|
|
||||
process\.|
|
||||
std\.
|
||||
|
||||
90
.travis.yml
90
.travis.yml
@@ -1,12 +1,6 @@
|
||||
language: python
|
||||
dist: xenial
|
||||
stages:
|
||||
- baseline
|
||||
- name: test
|
||||
if: repo = pytest-dev/pytest AND tag IS NOT present
|
||||
- name: deploy
|
||||
if: repo = pytest-dev/pytest AND tag IS present
|
||||
python: '3.7'
|
||||
dist: trusty
|
||||
python: '3.5.1'
|
||||
cache: false
|
||||
|
||||
env:
|
||||
@@ -22,77 +16,11 @@ install:
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# OSX tests - first (in test stage), since they are the slower ones.
|
||||
# Coverage for:
|
||||
# - osx
|
||||
# - verbose=1
|
||||
- os: osx
|
||||
osx_image: xcode10.1
|
||||
language: generic
|
||||
env: TOXENV=py37-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS=-v
|
||||
# Coverage for Python 3.5.{0,1} specific code, mostly typing related.
|
||||
- env: TOXENV=py35 PYTEST_COVERAGE=1 PYTEST_ADDOPTS="-k test_raises_cyclic_reference"
|
||||
before_install:
|
||||
- which python3
|
||||
- python3 -V
|
||||
- ln -sfn "$(which python3)" /usr/local/bin/python
|
||||
- python -V
|
||||
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37
|
||||
|
||||
# Full run of latest supported version, without xdist.
|
||||
# Coverage for:
|
||||
# - pytester's LsofFdLeakChecker
|
||||
# - TestArgComplete (linux only)
|
||||
# - numpy
|
||||
# - old attrs
|
||||
# - verbose=0
|
||||
# - test_sys_breakpoint_interception (via pexpect).
|
||||
- env: TOXENV=py37-lsof-numpy-oldattrs-pexpect-twisted PYTEST_COVERAGE=1 PYTEST_ADDOPTS=
|
||||
python: '3.7'
|
||||
|
||||
# Coverage tracking is slow with pypy, skip it.
|
||||
- env: TOXENV=pypy3-xdist
|
||||
python: 'pypy3'
|
||||
|
||||
- env: TOXENV=py35-xdist
|
||||
python: '3.5'
|
||||
|
||||
# Specialized factors for py37.
|
||||
- env: TOXENV=py37-pluggymaster-xdist
|
||||
- env: TOXENV=py37-freeze
|
||||
|
||||
- env: TOXENV=py38-xdist
|
||||
python: '3.8'
|
||||
|
||||
- stage: baseline
|
||||
env: TOXENV=py36-xdist
|
||||
python: '3.6'
|
||||
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pre-commit
|
||||
|
||||
- stage: deploy
|
||||
python: '3.6'
|
||||
install: pip install -U setuptools setuptools_scm tox
|
||||
script: skip
|
||||
# token to upload github release notes: GH_RELEASE_NOTES_TOKEN
|
||||
env:
|
||||
- secure: "OjOeL7/0JUDkV00SsTs732e8vQjHynpbG9FKTNtZZJ+1Zn4Cib+hAlwmlBnvVukML0X60YpcfjnC4quDOIGLPsh5zeXnvJmYtAIIUNQXjWz8NhcGYrhyzuP1rqV22U68RTCdmOq3lMYU/W2acwHP7T49PwJtOiUM5kF120UAQ0Zi5EmkqkIvH8oM5mO9Dlver+/U7Htpz9rhKrHBXQNCMZI6yj2aUyukqB2PN2fjAlDbCF//+FmvYw9NjT4GeFOSkTCf4ER9yfqs7yglRfwiLtOCZ2qKQhWZNsSJDB89rxIRXWavJUjJKeY2EW2/NkomYJDpqJLIF4JeFRw/HhA47CYPeo6BJqyyNV+0CovL1frpWfi9UQw2cMbgFUkUIUk3F6DD59PHNIOX2R/HX56dQsw7WKl3QuHlCOkICXYg8F7Ta684IoKjeTX03/6QNOkURfDBwfGszY0FpbxrjCSWKom6RyZdyidnESaxv9RzjcIRZVh1rp8KMrwS1OrwRSdG0zjlsPr49hWMenN/8fKgcHTV4/r1Tj6mip0dorSRCrgUNIeRBKgmui6FS8642ab5JNKOxMteVPVR2sFuhjOQ0Jy+PmvceYY9ZMWc3+/B/KVh0dZ3hwvLGZep/vxDS2PwCA5/xw31714vT5LxidKo8yECjBynMU/wUTTS695D3NY="
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# required by publish_gh_release_notes
|
||||
- pandoc
|
||||
after_deploy: tox -e publish_gh_release_notes
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: nicoddemus
|
||||
distributions: sdist bdist_wheel
|
||||
skip_upload_docs: true
|
||||
password:
|
||||
secure: xanTgTUu6XDQVqB/0bwJQXoDMnU5tkwZc5koz6mBkkqZhKdNOi2CLoC1XhiSZ+ah24l4V1E0GAqY5kBBcy9d7NVe4WNg4tD095LsHw+CRU6/HCVIFfyk2IZ+FPAlguesCcUiJSXOrlBF+Wj68wEvLoK7EoRFbJeiZ/f91Ww1sbtDlqXABWGHrmhPJL5Wva7o7+wG7JwJowqdZg1pbQExsCc7b53w4v2RBu3D6TJaTAzHiVsW+nUSI67vKI/uf+cR/OixsTfy37wlHgSwihYmrYLFls3V0bSpahCim3bCgMaFZx8S8xrdgJ++PzBCof2HeflFKvW+VCkoYzGEG4NrTWJoNz6ni4red9GdvfjGH3YCjAKS56h9x58zp2E5rpsb/kVq5/45xzV+dq6JRuhQ1nJWjBC6fSKAc/bfwnuFK3EBxNLkvBssLHvsNjj5XG++cB8DdS9wVGUqjpoK4puaXUWFqy4q3S9F86HEsKNgExtieA9qNx+pCIZVs6JCXZNjr0I5eVNzqJIyggNgJG6RyravsU35t9Zd9doL5g4Y7UKmAGTn1Sz24HQ4sMQgXdm2SyD8gEK5je4tlhUvfGtDvMSlstq71kIn9nRpFnqB6MFlbYSEAZmo8dGbCquoUc++6Rum208wcVbrzzVtGlXB/Ow9AbFMYeAGA0+N/K1e59c=
|
||||
on:
|
||||
tags: true
|
||||
repo: pytest-dev/pytest
|
||||
# Work around https://github.com/jaraco/zipp/issues/40.
|
||||
- python -m pip install -U 'setuptools>=34.4.0' virtualenv==16.7.9
|
||||
|
||||
before_script:
|
||||
- |
|
||||
@@ -113,7 +41,7 @@ script: tox
|
||||
after_success:
|
||||
- |
|
||||
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
|
||||
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh
|
||||
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh -F Travis
|
||||
fi
|
||||
|
||||
notifications:
|
||||
@@ -129,6 +57,4 @@ notifications:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- features
|
||||
- 4.6-maintenance
|
||||
- /^\d+(\.\d+)+$/
|
||||
- /^\d+\.\d+\.x$/
|
||||
|
||||
17
AUTHORS
17
AUTHORS
@@ -52,6 +52,7 @@ Carl Friedrich Bolz
|
||||
Carlos Jenkins
|
||||
Ceridwen
|
||||
Charles Cloud
|
||||
Charles Machalow
|
||||
Charnjit SiNGH (CCSJ)
|
||||
Chris Lamb
|
||||
Christian Boelsen
|
||||
@@ -59,12 +60,13 @@ Christian Fetzer
|
||||
Christian Neumüller
|
||||
Christian Theunert
|
||||
Christian Tismer
|
||||
Christopher Gilling
|
||||
Christoph Buelter
|
||||
Christopher Dignam
|
||||
Christopher Gilling
|
||||
Claudio Madotto
|
||||
CrazyMerlyn
|
||||
Cyrus Maden
|
||||
Damian Skrzypczak
|
||||
Dhiren Serai
|
||||
Daniel Grana
|
||||
Daniel Hahler
|
||||
Daniel Nuri
|
||||
@@ -79,6 +81,7 @@ David Szotten
|
||||
David Vierra
|
||||
Daw-Ran Liou
|
||||
Denis Kirisov
|
||||
Dhiren Serai
|
||||
Diego Russo
|
||||
Dmitry Dygalo
|
||||
Dmitry Pribysh
|
||||
@@ -111,6 +114,7 @@ Guido Wesdorp
|
||||
Guoqiang Zhang
|
||||
Harald Armin Massa
|
||||
Henk-Jaap Wagenaar
|
||||
Holger Kohr
|
||||
Hugo van Kemenade
|
||||
Hui Wang (coldnight)
|
||||
Ian Bicking
|
||||
@@ -119,6 +123,7 @@ Ilya Konstantinov
|
||||
Ionuț Turturică
|
||||
Iwan Briquemont
|
||||
Jaap Broekhuizen
|
||||
Jakub Mitoraj
|
||||
Jan Balster
|
||||
Janne Vanhala
|
||||
Jason R. Coombs
|
||||
@@ -140,6 +145,7 @@ Joshua Bronson
|
||||
Jurko Gospodnetić
|
||||
Justyna Janczyszyn
|
||||
Kale Kundert
|
||||
Karl O. Pinc
|
||||
Katarzyna Jachim
|
||||
Katerina Koukiou
|
||||
Kevin Cox
|
||||
@@ -189,6 +195,7 @@ Mihai Capotă
|
||||
Mike Hoyle (hoylemd)
|
||||
Mike Lundy
|
||||
Miro Hrončok
|
||||
Nathaniel Compton
|
||||
Nathaniel Waisbrot
|
||||
Ned Batchelder
|
||||
Neven Mundar
|
||||
@@ -205,8 +212,10 @@ Omer Hadari
|
||||
Ondřej Súkup
|
||||
Oscar Benjamin
|
||||
Patrick Hayes
|
||||
Pauli Virtanen
|
||||
Paweł Adamczak
|
||||
Pedro Algarvio
|
||||
Philipp Loose
|
||||
Pieter Mulder
|
||||
Piotr Banaszkiewicz
|
||||
Pulkit Goyal
|
||||
@@ -234,10 +243,12 @@ Samuele Pedroni
|
||||
Sankt Petersbug
|
||||
Segev Finer
|
||||
Serhii Mozghovyi
|
||||
Seth Junot
|
||||
Simon Gomizelj
|
||||
Skylar Downes
|
||||
Srinivas Reddy Thatiparthy
|
||||
Stefan Farmbauer
|
||||
Stefan Scherfke
|
||||
Stefan Zimmermann
|
||||
Stefano Taschini
|
||||
Steffen Allner
|
||||
@@ -254,6 +265,7 @@ Tim Hoffmann
|
||||
Tim Strazny
|
||||
Tom Dalton
|
||||
Tom Viner
|
||||
Tomáš Gavenčiak
|
||||
Tomer Keren
|
||||
Trevor Bekolay
|
||||
Tyler Goodlet
|
||||
@@ -265,6 +277,7 @@ Vidar T. Fauske
|
||||
Virgil Dupras
|
||||
Vitaly Lashmanov
|
||||
Vlad Dragos
|
||||
Vladyslav Rachek
|
||||
Volodymyr Piskun
|
||||
Wei Lin
|
||||
Wil Cooley
|
||||
|
||||
7553
CHANGELOG.rst
7553
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@@ -71,7 +71,6 @@ contacted individually:
|
||||
- Brianna Laugher ([@pfctdayelise](https://github.com/pfctdayelise)): brianna@laugher.id.au
|
||||
- Bruno Oliveira ([@nicoddemus](https://github.com/nicoddemus)): nicoddemus@gmail.com
|
||||
- Florian Bruhin ([@the-compiler](https://github.com/the-compiler)): pytest@the-compiler.org
|
||||
- Ronny Pfannschmidt ([@RonnyPfannschmidt](https://github.com/RonnyPfannschmidt)): ich@ronnypfannschmidt.de
|
||||
|
||||
## Attribution
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@ Fix bugs
|
||||
|
||||
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
|
||||
|
||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
|
||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs. To indicate that you are going
|
||||
to work on a particular issue, add a comment to that effect on the specific issue.
|
||||
|
||||
Don't forget to check the issue trackers of your favourite plugins, too!
|
||||
|
||||
@@ -85,7 +86,7 @@ without using a local copy. This can be convenient for small fixes.
|
||||
|
||||
$ tox -e docs
|
||||
|
||||
The built documentation should be available in the ``doc/en/_build/``.
|
||||
The built documentation should be available in ``doc/en/_build/html``.
|
||||
|
||||
Where 'en' refers to the documentation language.
|
||||
|
||||
@@ -165,8 +166,6 @@ Short version
|
||||
|
||||
#. Fork the repository.
|
||||
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
|
||||
#. Target ``master`` for bugfixes and doc changes.
|
||||
#. Target ``features`` for new features or functionality changes.
|
||||
#. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting.
|
||||
#. Tests are run using ``tox``::
|
||||
|
||||
@@ -203,15 +202,11 @@ Here is a simple overview, with pytest-specific bits:
|
||||
|
||||
$ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git
|
||||
$ cd pytest
|
||||
# now, to fix a bug create your own branch off "master":
|
||||
# now, create your own branch off "master":
|
||||
|
||||
$ git checkout -b your-bugfix-branch-name master
|
||||
|
||||
# or to instead add a feature create your own branch off "features":
|
||||
|
||||
$ git checkout -b your-feature-branch-name features
|
||||
|
||||
Given we have "major.minor.micro" version numbers, bugfixes will usually
|
||||
Given we have "major.minor.micro" version numbers, bug fixes will usually
|
||||
be released in micro releases whereas features will be released in
|
||||
minor releases and incompatible changes in major releases.
|
||||
|
||||
@@ -293,8 +288,7 @@ Here is a simple overview, with pytest-specific bits:
|
||||
compare: your-branch-name
|
||||
|
||||
base-fork: pytest-dev/pytest
|
||||
base: master # if it's a bugfix
|
||||
base: features # if it's a feature
|
||||
base: master
|
||||
|
||||
|
||||
Writing Tests
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
Release Procedure
|
||||
-----------------
|
||||
|
||||
Our current policy for releasing is to aim for a bugfix every few weeks and a minor release every 2-3 months. The idea
|
||||
is to get fixes and new features out instead of trying to cram a ton of features into a release and by consequence
|
||||
taking a lot of time to make a new one.
|
||||
|
||||
.. important::
|
||||
|
||||
pytest releases must be prepared on **Linux** because the docs and examples expect
|
||||
to be executed in that platform.
|
||||
|
||||
#. Create a branch ``release-X.Y.Z`` with the version for the release.
|
||||
|
||||
* **maintenance releases**: from ``4.6-maintenance``;
|
||||
|
||||
* **patch releases**: from the latest ``master``;
|
||||
|
||||
* **minor releases**: from the latest ``features``; then merge with the latest ``master``;
|
||||
|
||||
Ensure your are in a clean work tree.
|
||||
|
||||
#. Using ``tox``, generate docs, changelog, announcements::
|
||||
|
||||
$ tox -e release -- <VERSION>
|
||||
|
||||
This will generate a commit with all the changes ready for pushing.
|
||||
|
||||
#. Open a PR for this branch targeting ``master`` (or ``4.6-maintenance`` for
|
||||
maintenance releases).
|
||||
|
||||
#. After all tests pass and the PR has been approved, publish to PyPI by pushing the tag::
|
||||
|
||||
git tag <VERSION>
|
||||
git push git@github.com:pytest-dev/pytest.git <VERSION>
|
||||
|
||||
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
|
||||
|
||||
#. Merge the PR.
|
||||
|
||||
#. If this is a maintenance release, cherry-pick the CHANGELOG / announce
|
||||
files to the ``master`` branch::
|
||||
|
||||
git fetch --all --prune
|
||||
git checkout origin/master -b cherry-pick-maintenance-release
|
||||
git cherry-pick --no-commit -m1 origin/4.6-maintenance
|
||||
git checkout origin/master -- changelog
|
||||
git commit # no arguments
|
||||
|
||||
#. Send an email announcement with the contents from::
|
||||
|
||||
doc/en/announce/release-<VERSION>.rst
|
||||
|
||||
To the following mailing lists:
|
||||
|
||||
* pytest-dev@python.org (all releases)
|
||||
* python-announce-list@python.org (all releases)
|
||||
* testing-in-python@lists.idyll.org (only major/minor releases)
|
||||
|
||||
And announce it on `Twitter <https://twitter.com/>`_ with the ``#pytest`` hashtag.
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2019 Holger Krekel and others
|
||||
Copyright (c) 2004-2020 Holger Krekel and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
12
README.rst
12
README.rst
@@ -31,6 +31,10 @@
|
||||
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
||||
:target: https://www.codetriage.com/pytest-dev/pytest
|
||||
|
||||
.. image:: https://readthedocs.org/projects/pytest/badge/?version=latest
|
||||
:target: https://pytest.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
The ``pytest`` framework makes it easy to write small tests, yet
|
||||
scales to support complex functional testing for applications and libraries.
|
||||
|
||||
@@ -112,12 +116,12 @@ Support pytest
|
||||
--------------
|
||||
|
||||
`Open Collective`_ is an online funding platform for open and transparent communities.
|
||||
It provide tools to raise money and share your finances in full transparency.
|
||||
It provides tools to raise money and share your finances in full transparency.
|
||||
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
See more datails in the `pytest collective`_.
|
||||
See more details in the `pytest collective`_.
|
||||
|
||||
.. _Open Collective: https://opencollective.com
|
||||
.. _pytest collective: https://opencollective.com/pytest
|
||||
@@ -137,7 +141,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
pytest has never been associated with a security vulnerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
@@ -145,7 +149,7 @@ Tidelift will coordinate the fix and disclosure.
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2019.
|
||||
Copyright Holger Krekel and others, 2004-2020.
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
|
||||
82
RELEASING.rst
Normal file
82
RELEASING.rst
Normal file
@@ -0,0 +1,82 @@
|
||||
Release Procedure
|
||||
-----------------
|
||||
|
||||
Our current policy for releasing is to aim for a bug-fix release every few weeks and a minor release every 2-3 months. The idea
|
||||
is to get fixes and new features out instead of trying to cram a ton of features into a release and by consequence
|
||||
taking a lot of time to make a new one.
|
||||
|
||||
Preparing: Automatic Method
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We have developed an automated workflow for releases, that uses GitHub workflows and is triggered
|
||||
by opening an issue or issuing a comment one.
|
||||
|
||||
The comment must be in the form::
|
||||
|
||||
@pytestbot please prepare release from BRANCH
|
||||
|
||||
Where ``BRANCH`` is ``master`` or one of the maintenance branches.
|
||||
|
||||
After that, the workflow should publish a PR and notify that it has done so as a comment
|
||||
in the original issue.
|
||||
|
||||
Preparing: Manual Method
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. important::
|
||||
|
||||
pytest releases must be prepared on **Linux** because the docs and examples expect
|
||||
to be executed on that platform.
|
||||
|
||||
To release a version ``MAJOR.MINOR.PATCH``, follow these steps:
|
||||
|
||||
#. For major and minor releases, create a new branch ``MAJOR.MINOR.x`` from the
|
||||
latest ``master`` and push it to the ``pytest-dev/pytest`` repo.
|
||||
|
||||
#. Create a branch ``release-MAJOR.MINOR.PATCH`` from the ``MAJOR.MINOR.x`` branch.
|
||||
|
||||
Ensure your are updated and in a clean working tree.
|
||||
|
||||
#. Using ``tox``, generate docs, changelog, announcements::
|
||||
|
||||
$ tox -e release -- MAJOR.MINOR.PATCH
|
||||
|
||||
This will generate a commit with all the changes ready for pushing.
|
||||
|
||||
#. Open a PR for the ``release-MAJOR.MINOR.PATCH`` branch targeting ``MAJOR.MINOR.x``.
|
||||
|
||||
|
||||
Releasing
|
||||
~~~~~~~~~
|
||||
|
||||
Both automatic and manual processes described above follow the same steps from this point onward.
|
||||
|
||||
#. After all tests pass and the PR has been approved, tag the release commit
|
||||
in the ``MAJOR.MINOR.x`` branch and push it. This will publish to PyPI::
|
||||
|
||||
git tag MAJOR.MINOR.PATCH
|
||||
git push git@github.com:pytest-dev/pytest.git MAJOR.MINOR.PATCH
|
||||
|
||||
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
|
||||
|
||||
#. Merge the PR.
|
||||
|
||||
#. Cherry-pick the CHANGELOG / announce files to the ``master`` branch::
|
||||
|
||||
git fetch --all --prune
|
||||
git checkout origin/master -b cherry-pick-release
|
||||
git cherry-pick --no-commit -m1 origin/MAJOR.MINOR.x
|
||||
git checkout origin/master -- changelog
|
||||
git commit # no arguments
|
||||
|
||||
#. Send an email announcement with the contents from::
|
||||
|
||||
doc/en/announce/release-<VERSION>.rst
|
||||
|
||||
To the following mailing lists:
|
||||
|
||||
* pytest-dev@python.org (all releases)
|
||||
* python-announce-list@python.org (all releases)
|
||||
* testing-in-python@lists.idyll.org (only major/minor releases)
|
||||
|
||||
And announce it on `Twitter <https://twitter.com/>`_ with the ``#pytest`` hashtag.
|
||||
@@ -1,77 +0,0 @@
|
||||
trigger:
|
||||
- master
|
||||
- features
|
||||
|
||||
variables:
|
||||
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv"
|
||||
PYTEST_COVERAGE: '0'
|
||||
|
||||
jobs:
|
||||
|
||||
- job: 'Test'
|
||||
pool:
|
||||
vmImage: "vs2017-win2016"
|
||||
strategy:
|
||||
matrix:
|
||||
# -- pypy3 disabled for now: #5279 --
|
||||
# pypy3:
|
||||
# python.version: 'pypy3'
|
||||
# tox.env: 'pypy3'
|
||||
py35-xdist:
|
||||
python.version: '3.5'
|
||||
tox.env: 'py35-xdist'
|
||||
# Coverage for:
|
||||
# - test_supports_breakpoint_module_global
|
||||
PYTEST_COVERAGE: '1'
|
||||
py36-xdist:
|
||||
python.version: '3.6'
|
||||
tox.env: 'py36-xdist'
|
||||
py37:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-twisted-numpy'
|
||||
# Coverage for:
|
||||
# - _py36_windowsconsoleio_workaround (with py36+)
|
||||
# - test_request_garbage (no xdist)
|
||||
PYTEST_COVERAGE: '1'
|
||||
py37-linting/docs/doctesting:
|
||||
python.version: '3.7'
|
||||
tox.env: 'linting,docs,doctesting'
|
||||
py37-pluggymaster-xdist:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-pluggymaster-xdist'
|
||||
maxParallel: 10
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(python.version)'
|
||||
architecture: 'x64'
|
||||
|
||||
- script: python -m pip install --upgrade pip && python -m pip install tox
|
||||
displayName: 'Install tox'
|
||||
|
||||
- bash: |
|
||||
if [[ "$PYTEST_COVERAGE" == "1" ]]; then
|
||||
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
|
||||
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
export COVERAGE_FILE="$PWD/.coverage"
|
||||
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
|
||||
fi
|
||||
python -m tox -e $(tox.env)
|
||||
displayName: 'Run tests'
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFiles: 'build/test-results/$(tox.env).xml'
|
||||
testRunTitle: '$(tox.env)'
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- bash: |
|
||||
if [[ "$PYTEST_COVERAGE" == 1 ]]; then
|
||||
scripts/report-coverage.sh
|
||||
fi
|
||||
env:
|
||||
CODECOV_NAME: $(tox.env)
|
||||
CODECOV_TOKEN: $(CODECOV_TOKEN)
|
||||
displayName: Report and upload coverage
|
||||
condition: eq(variables['PYTEST_COVERAGE'], '1')
|
||||
@@ -15,10 +15,10 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
|
||||
|
||||
* ``feature``: new user facing features, like new command-line options and new behavior.
|
||||
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junitxml``, improved colors in terminal, etc).
|
||||
* ``bugfix``: fixes a reported bug.
|
||||
* ``bugfix``: fixes a bug.
|
||||
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
|
||||
* ``deprecation``: feature deprecation.
|
||||
* ``removal``: feature removal.
|
||||
* ``breaking``: a change which may break existing suites, such as feature removal or behavior change.
|
||||
* ``vendor``: changes in packages vendored in pytest.
|
||||
* ``trivial``: fixing a small typo or internal change that might be noteworthy.
|
||||
|
||||
@@ -31,6 +31,7 @@ changelog using that instead.
|
||||
If you are not sure what issue type to use, don't hesitate to ask in your PR.
|
||||
|
||||
``towncrier`` preserves multiple paragraphs and formatting (code blocks, lists, and so on), but for entries
|
||||
other than ``features`` it is usually better to stick to a single paragraph to keep it concise. You can install
|
||||
``towncrier`` and then run ``towncrier --draft``
|
||||
if you want to get a preview of how your change will look in the final release notes.
|
||||
other than ``features`` it is usually better to stick to a single paragraph to keep it concise.
|
||||
|
||||
You can also run ``tox -e docs`` to build the documentation
|
||||
with the draft changelog (``doc/en/_build/html/changelog.html``) if you want to get a preview of how your change will look in the final release notes.
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
coverage:
|
||||
status:
|
||||
project: true
|
||||
patch: true
|
||||
changes: true
|
||||
|
||||
comment: off
|
||||
|
||||
150
doc/en/Makefile
150
doc/en/Makefile
@@ -1,16 +1,24 @@
|
||||
# Makefile for Sphinx documentation
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
|
||||
REGENDOC_ARGS := \
|
||||
--normalize "/[ \t]+\n/\n/" \
|
||||
@@ -25,130 +33,8 @@ REGENDOC_ARGS := \
|
||||
--normalize "@hypothesis-(\d+)\\.[.\d,]+@hypothesis-\1.x.y@" \
|
||||
--normalize "@Python (\d+)\\.[^ ,]+@Python \1.x.y@"
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " showtarget to show the pytest.org target directory"
|
||||
@echo " install to install docs to pytest.org/SITETARGET"
|
||||
@echo " install-ldf to install the doc pdf to pytest.org/SITETARGET"
|
||||
@echo " regen to regenerate pytest examples using the installed pytest"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
regen: REGENDOC_FILES:=*.rst */*.rst
|
||||
regen:
|
||||
PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPTS="-pno:hypothesis -Wignore::pytest.PytestUnknownMarkWarning" COLUMNS=76 regendoc --update ${REGENDOC_FILES} ${REGENDOC_ARGS}
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pytest.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pytest.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/pytest"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pytest"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
texinfo:
|
||||
mkdir -p $(BUILDDIR)/texinfo
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
mkdir -p $(BUILDDIR)/texinfo
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
.PHONY: regen
|
||||
|
||||
@@ -21,3 +21,7 @@
|
||||
<hr>
|
||||
{{ toc }}
|
||||
{%- endif %}
|
||||
|
||||
<hr>
|
||||
<a href="{{ pathto('genindex') }}">Index</a>
|
||||
<hr>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{% extends "!layout.html" %}
|
||||
{% block header %}
|
||||
{{super()}}
|
||||
{% endblock %}
|
||||
{% block footer %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-7597274-13']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -6,6 +6,13 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-5.4.2
|
||||
release-5.4.1
|
||||
release-5.4.0
|
||||
release-5.3.5
|
||||
release-5.3.4
|
||||
release-5.3.3
|
||||
release-5.3.2
|
||||
release-5.3.1
|
||||
release-5.3.0
|
||||
release-5.2.4
|
||||
@@ -19,6 +26,10 @@ Release announcements
|
||||
release-5.1.0
|
||||
release-5.0.1
|
||||
release-5.0.0
|
||||
release-4.6.9
|
||||
release-4.6.8
|
||||
release-4.6.7
|
||||
release-4.6.6
|
||||
release-4.6.5
|
||||
release-4.6.4
|
||||
release-4.6.3
|
||||
|
||||
@@ -3,13 +3,13 @@ pytest-2.3: improved fixtures / better unittest integration
|
||||
|
||||
pytest-2.3 comes with many major improvements for fixture/funcarg management
|
||||
and parametrized testing in Python. It is now easier, more efficient and
|
||||
more predicatable to re-run the same tests with different fixture
|
||||
more predictable to re-run the same tests with different fixture
|
||||
instances. Also, you can directly declare the caching "scope" of
|
||||
fixtures so that dependent tests throughout your whole test suite can
|
||||
re-use database or other expensive fixture objects with ease. Lastly,
|
||||
it's possible for fixture functions (formerly known as funcarg
|
||||
factories) to use other fixtures, allowing for a completely modular and
|
||||
re-useable fixture design.
|
||||
re-usable fixture design.
|
||||
|
||||
For detailed info and tutorial-style examples, see:
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ holger krekel
|
||||
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
|
||||
are used with test methods. This fixes issue346 and should
|
||||
help with random "xdist" collection failures. Thanks to
|
||||
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ holger krekel
|
||||
- fix issue435: make reload() work when assert rewriting is active.
|
||||
Thanks Daniel Hahler.
|
||||
|
||||
- fix issue616: conftest.py files and their contained fixutres are now
|
||||
- fix issue616: conftest.py files and their contained fixtures are now
|
||||
properly considered for visibility, independently from the exact
|
||||
current working directory and test arguments that are used.
|
||||
Many thanks to Eric Siegerman and his PR235 which contains
|
||||
|
||||
20
doc/en/announce/release-4.6.6.rst
Normal file
20
doc/en/announce/release-4.6.6.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-4.6.6
|
||||
=======================================
|
||||
|
||||
pytest 4.6.6 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Michael Goerz
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
19
doc/en/announce/release-4.6.7.rst
Normal file
19
doc/en/announce/release-4.6.7.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
pytest-4.6.7
|
||||
=======================================
|
||||
|
||||
pytest 4.6.7 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
20
doc/en/announce/release-4.6.8.rst
Normal file
20
doc/en/announce/release-4.6.8.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-4.6.8
|
||||
=======================================
|
||||
|
||||
pytest 4.6.8 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Ryan Mast
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
21
doc/en/announce/release-4.6.9.rst
Normal file
21
doc/en/announce/release-4.6.9.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-4.6.9
|
||||
=======================================
|
||||
|
||||
pytest 4.6.9 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Felix Yan
|
||||
* Hugo
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
26
doc/en/announce/release-5.3.2.rst
Normal file
26
doc/en/announce/release-5.3.2.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
pytest-5.3.2
|
||||
=======================================
|
||||
|
||||
pytest 5.3.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Claudio Madotto
|
||||
* Daniel Hahler
|
||||
* Jared Vasquez
|
||||
* Michael Rose
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
30
doc/en/announce/release-5.3.3.rst
Normal file
30
doc/en/announce/release-5.3.3.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
pytest-5.3.3
|
||||
=======================================
|
||||
|
||||
pytest 5.3.3 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Adam Johnson
|
||||
* Alexandre Mulatinho
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Chris NeJame
|
||||
* Daniel Hahler
|
||||
* Hugo van Kemenade
|
||||
* Marcelo Duarte Trevisani
|
||||
* PaulC
|
||||
* Ran Benita
|
||||
* Ryan Barner
|
||||
* Seth Junot
|
||||
* marc
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
20
doc/en/announce/release-5.3.4.rst
Normal file
20
doc/en/announce/release-5.3.4.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-5.3.4
|
||||
=======================================
|
||||
|
||||
pytest 5.3.4 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Ran Benita
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
19
doc/en/announce/release-5.3.5.rst
Normal file
19
doc/en/announce/release-5.3.5.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
pytest-5.3.5
|
||||
=======================================
|
||||
|
||||
pytest 5.3.5 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Daniel Hahler
|
||||
* Ran Benita
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
59
doc/en/announce/release-5.4.0.rst
Normal file
59
doc/en/announce/release-5.4.0.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
pytest-5.4.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 5.4.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bug fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from PyPI via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Christoph Buelter
|
||||
* Christoph Bülter
|
||||
* Daniel Arndt
|
||||
* Daniel Hahler
|
||||
* Holger Kohr
|
||||
* Hugo
|
||||
* Hugo van Kemenade
|
||||
* Jakub Mitoraj
|
||||
* Kyle Altendorf
|
||||
* Minuddin Ahmed Rana
|
||||
* Nathaniel Compton
|
||||
* ParetoLife
|
||||
* Pauli Virtanen
|
||||
* Philipp Loose
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
* Stefan Scherfke
|
||||
* Stefano Mazzucco
|
||||
* TWood67
|
||||
* Tobias Schmidt
|
||||
* Tomáš Gavenčiak
|
||||
* Vinay Calastry
|
||||
* Vladyslav Rachek
|
||||
* Zac Hatfield-Dodds
|
||||
* captainCapitalism
|
||||
* cmachalo
|
||||
* gftea
|
||||
* kpinc
|
||||
* rebecca-palmer
|
||||
* sdementen
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
18
doc/en/announce/release-5.4.1.rst
Normal file
18
doc/en/announce/release-5.4.1.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
pytest-5.4.1
|
||||
=======================================
|
||||
|
||||
pytest 5.4.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Bruno Oliveira
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
22
doc/en/announce/release-5.4.2.rst
Normal file
22
doc/en/announce/release-5.4.2.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
pytest-5.4.2
|
||||
=======================================
|
||||
|
||||
pytest 5.4.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -47,6 +47,8 @@ you will see the return value of the function call:
|
||||
E + where 3 = f()
|
||||
|
||||
test_assert1.py:6: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_assert1.py::test_function - assert 3 == 4
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
``pytest`` has support for showing the values of the most common subexpressions
|
||||
@@ -208,6 +210,8 @@ if you run this module:
|
||||
E Use -v to get the full diff
|
||||
|
||||
test_assert2.py:6: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_assert2.py::test_set_comparison - AssertionError: assert {'0'...
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
Special comparisons are done for a number of cases:
|
||||
@@ -279,6 +283,8 @@ the conftest file:
|
||||
E vals: 1 != 2
|
||||
|
||||
test_foocompare.py:12: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_foocompare.py::test_compare - assert Comparing Foo instances:
|
||||
1 failed in 0.12s
|
||||
|
||||
.. _assert-details:
|
||||
|
||||
@@ -3,6 +3,61 @@
|
||||
Backwards Compatibility Policy
|
||||
==============================
|
||||
|
||||
.. versionadded: 6.0
|
||||
|
||||
pytest is actively evolving and is a project that has been decades in the making,
|
||||
we keep learning about new and better structures to express different details about testing.
|
||||
|
||||
While we implement those modifications we try to ensure an easy transition and don't want to impose unnecessary churn on our users and community/plugin authors.
|
||||
|
||||
As of now, pytest considers multipe types of backward compatibility transitions:
|
||||
|
||||
a) trivial: APIs which trivially translate to the new mechanism,
|
||||
and do not cause problematic changes.
|
||||
|
||||
We try to support those indefinitely while encouraging users to switch to newer/better mechanisms through documentation.
|
||||
|
||||
b) transitional: the old and new API don't conflict
|
||||
and we can help users transition by using warnings, while supporting both for a prolonged time.
|
||||
|
||||
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
|
||||
|
||||
When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn them into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed.
|
||||
|
||||
|
||||
c) true breakage: should only to be considered when normal transition is unreasonably unsustainable and would offset important development/features by years.
|
||||
In addition, they should be limited to APIs where the number of actual users is very small (for example only impacting some plugins), and can be coordinated with the community in advance.
|
||||
|
||||
Examples for such upcoming changes:
|
||||
|
||||
* removal of ``pytest_runtest_protocol/nextitem`` - `#895`_
|
||||
* rearranging of the node tree to include ``FunctionDefinition``
|
||||
* rearranging of ``SetupState`` `#895`_
|
||||
|
||||
True breakages must be announced first in an issue containing:
|
||||
|
||||
* Detailed description of the change
|
||||
* Rationale
|
||||
* Expected impact on users and plugin authors (example in `#895`_)
|
||||
|
||||
After there's no hard *-1* on the issue it should be followed up by an initial proof-of-concept Pull Request.
|
||||
|
||||
This POC serves as both a coordination point to assess impact and potential inspriation to come up with a transitional solution after all.
|
||||
|
||||
After a reasonable amount of time the PR can be merged to base a new major release.
|
||||
|
||||
For the PR to mature from POC to acceptance, it must contain:
|
||||
* Setup of deprecation errors/warnings that help users fix and port their code. If it is possible to introduce a deprecation period under the current series, before the true breakage, it should be introduced in a separate PR and be part of the current release stream.
|
||||
* Detailed description of the rationale and examples on how to port code in ``doc/en/deprecations.rst``.
|
||||
|
||||
|
||||
History
|
||||
=========
|
||||
|
||||
|
||||
Focus primary on smooth transition - stance (pre 6.0)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Keeping backwards compatibility has a very high priority in the pytest project. Although we have deprecated functionality over the years, most of it is still supported. All deprecations in pytest were done because simpler or more efficient ways of accomplishing the same tasks have emerged, making the old way of doing things unnecessary.
|
||||
|
||||
With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
|
||||
@@ -20,3 +75,6 @@ Deprecation Roadmap
|
||||
Features currently deprecated and removed in previous releases can be found in :ref:`deprecations`.
|
||||
|
||||
We track future deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.
|
||||
|
||||
|
||||
.. _`#895`: https://github.com/pytest-dev/pytest/issues/895
|
||||
|
||||
@@ -137,9 +137,11 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
tmpdir_factory [session scope]
|
||||
Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
|
||||
|
||||
|
||||
tmp_path_factory [session scope]
|
||||
Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
|
||||
|
||||
|
||||
tmpdir
|
||||
Return a temporary directory path object
|
||||
which is unique to each test function invocation,
|
||||
|
||||
@@ -75,6 +75,9 @@ If you run this for the first time you will see two failures:
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:7: Failed
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_50.py::test_num[17] - Failed: bad luck
|
||||
FAILED test_50.py::test_num[25] - Failed: bad luck
|
||||
2 failed, 48 passed in 0.12s
|
||||
|
||||
If you then run it with ``--lf``:
|
||||
@@ -86,7 +89,7 @@ If you then run it with ``--lf``:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 50 items / 48 deselected / 2 selected
|
||||
collected 2 items
|
||||
run-last-failure: rerun previous 2 failures
|
||||
|
||||
test_50.py FF [100%]
|
||||
@@ -114,7 +117,10 @@ If you then run it with ``--lf``:
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:7: Failed
|
||||
===================== 2 failed, 48 deselected in 0.12s =====================
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_50.py::test_num[17] - Failed: bad luck
|
||||
FAILED test_50.py::test_num[25] - Failed: bad luck
|
||||
============================ 2 failed in 0.12s =============================
|
||||
|
||||
You have run only the two failing tests from the last run, while the 48 passing
|
||||
tests have not been run ("deselected").
|
||||
@@ -158,6 +164,9 @@ of ``FF`` and dots):
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:7: Failed
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_50.py::test_num[17] - Failed: bad luck
|
||||
FAILED test_50.py::test_num[25] - Failed: bad luck
|
||||
======================= 2 failed, 48 passed in 0.12s =======================
|
||||
|
||||
.. _`config.cache`:
|
||||
@@ -230,6 +239,8 @@ If you run this command for the first time, you can see the print statement:
|
||||
test_caching.py:20: AssertionError
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
running expensive computation...
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_caching.py::test_function - assert 42 == 23
|
||||
1 failed in 0.12s
|
||||
|
||||
If you run it a second time, the value will be retrieved from
|
||||
@@ -249,9 +260,11 @@ the cache and nothing will be printed:
|
||||
E assert 42 == 23
|
||||
|
||||
test_caching.py:20: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_caching.py::test_function - assert 42 == 23
|
||||
1 failed in 0.12s
|
||||
|
||||
See the :ref:`cache-api` for more details.
|
||||
See the :fixture:`config.cache fixture <config.cache>` for more details.
|
||||
|
||||
|
||||
Inspecting Cache content
|
||||
|
||||
@@ -21,27 +21,36 @@ file descriptors. This allows to capture output from simple
|
||||
print statements as well as output from a subprocess started by
|
||||
a test.
|
||||
|
||||
.. _capture-method:
|
||||
|
||||
Setting capturing methods or disabling capturing
|
||||
-------------------------------------------------
|
||||
|
||||
There are two ways in which ``pytest`` can perform capturing:
|
||||
There are three ways in which ``pytest`` can perform capturing:
|
||||
|
||||
* file descriptor (FD) level capturing (default): All writes going to the
|
||||
* ``fd`` (file descriptor) level capturing (default): All writes going to the
|
||||
operating system file descriptors 1 and 2 will be captured.
|
||||
|
||||
* ``sys`` level capturing: Only writes to Python files ``sys.stdout``
|
||||
and ``sys.stderr`` will be captured. No capturing of writes to
|
||||
filedescriptors is performed.
|
||||
|
||||
* ``tee-sys`` capturing: Python writes to ``sys.stdout`` and ``sys.stderr``
|
||||
will be captured, however the writes will also be passed-through to
|
||||
the actual ``sys.stdout`` and ``sys.stderr``. This allows output to be
|
||||
'live printed' and captured for plugin use, such as junitxml (new in pytest 5.4).
|
||||
|
||||
.. _`disable capturing`:
|
||||
|
||||
You can influence output capturing mechanisms from the command line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest -s # disable all capturing
|
||||
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
|
||||
pytest --capture=fd # also point filedescriptors 1 and 2 to temp file
|
||||
pytest -s # disable all capturing
|
||||
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
|
||||
pytest --capture=fd # also point filedescriptors 1 and 2 to temp file
|
||||
pytest --capture=tee-sys # combines 'sys' and '-s', capturing sys.stdout/stderr
|
||||
# and passing it along to the actual sys.stdout/stderr
|
||||
|
||||
.. _printdebugging:
|
||||
|
||||
@@ -91,6 +100,8 @@ of the failing function and hide the other one:
|
||||
test_module.py:12: AssertionError
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
setting up <function test_func2 at 0xdeadbeef>
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_func2 - assert False
|
||||
======================= 1 failed, 1 passed in 0.12s ========================
|
||||
|
||||
Accessing captured output from a test function
|
||||
@@ -159,5 +170,3 @@ as a context manager, disabling capture inside the ``with`` block:
|
||||
with capsys.disabled():
|
||||
print("output not captured, going directly to sys.stdout")
|
||||
print("this output is also captured")
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
7945
doc/en/changelog.rst
7945
doc/en/changelog.rst
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,11 @@ import os
|
||||
import sys
|
||||
|
||||
from _pytest import __version__ as version
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sphinx.application
|
||||
|
||||
|
||||
release = ".".join(version.split(".")[:2])
|
||||
|
||||
@@ -62,7 +67,7 @@ master_doc = "contents"
|
||||
|
||||
# General information about the project.
|
||||
project = "pytest"
|
||||
copyright = "2015–2019, holger krekel and pytest-dev team"
|
||||
copyright = "2015–2020, holger krekel and pytest-dev team"
|
||||
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
@@ -78,7 +83,6 @@ copyright = "2015–2019, holger krekel and pytest-dev team"
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = [
|
||||
"links.inc",
|
||||
"_build",
|
||||
"naming20.rst",
|
||||
"test/*",
|
||||
@@ -157,7 +161,7 @@ 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 = "img/pytest1favi.ico"
|
||||
html_favicon = "img/favicon.png"
|
||||
|
||||
# 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,
|
||||
@@ -203,7 +207,7 @@ html_sidebars = {
|
||||
html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
html_use_index = False
|
||||
html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
@@ -285,7 +289,7 @@ man_pages = [("usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"
|
||||
epub_title = "pytest"
|
||||
epub_author = "holger krekel at merlinux eu"
|
||||
epub_publisher = "holger krekel at merlinux eu"
|
||||
epub_copyright = "2013, holger krekel et alii"
|
||||
epub_copyright = "2013-2020, holger krekel et alii"
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
@@ -342,12 +346,44 @@ texinfo_documents = [
|
||||
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
|
||||
|
||||
|
||||
def setup(app):
|
||||
def configure_logging(app: "sphinx.application.Sphinx") -> None:
|
||||
"""Configure Sphinx's WarningHandler to handle (expected) missing include."""
|
||||
import sphinx.util.logging
|
||||
import logging
|
||||
|
||||
class WarnLogFilter(logging.Filter):
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
"""Ignore warnings about missing include with "only" directive.
|
||||
|
||||
Ref: https://github.com/sphinx-doc/sphinx/issues/2150."""
|
||||
if (
|
||||
record.msg.startswith('Problems with "include" directive path:')
|
||||
and "_changelog_towncrier_draft.rst" in record.msg
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
logger = logging.getLogger(sphinx.util.logging.NAMESPACE)
|
||||
warn_handler = [x for x in logger.handlers if x.level == logging.WARNING]
|
||||
assert len(warn_handler) == 1, warn_handler
|
||||
warn_handler[0].filters.insert(0, WarnLogFilter())
|
||||
|
||||
|
||||
def setup(app: "sphinx.application.Sphinx") -> None:
|
||||
# from sphinx.ext.autodoc import cut_lines
|
||||
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
||||
app.add_crossref_type(
|
||||
"fixture",
|
||||
"fixture",
|
||||
objname="built-in fixture",
|
||||
indextemplate="pair: %s; fixture",
|
||||
)
|
||||
|
||||
app.add_object_type(
|
||||
"confval",
|
||||
"confval",
|
||||
objname="configuration value",
|
||||
indextemplate="pair: %s; configuration value",
|
||||
)
|
||||
|
||||
configure_logging(app)
|
||||
|
||||
@@ -20,8 +20,6 @@ which were registered by installed plugins.
|
||||
Initialization: determining rootdir and inifile
|
||||
-----------------------------------------------
|
||||
|
||||
|
||||
|
||||
pytest determines a ``rootdir`` for each test run which depends on
|
||||
the command line arguments (specified test files, paths) and on
|
||||
the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
|
||||
@@ -30,17 +28,17 @@ printed as part of the pytest header during startup.
|
||||
Here's a summary what ``pytest`` uses ``rootdir`` for:
|
||||
|
||||
* Construct *nodeids* during collection; each test is assigned
|
||||
a unique *nodeid* which is rooted at the ``rootdir`` and takes in account full path,
|
||||
class name, function name and parametrization (if any).
|
||||
a unique *nodeid* which is rooted at the ``rootdir`` and takes into account
|
||||
the full path, class name, function name and parametrization (if any).
|
||||
|
||||
* Is used by plugins as a stable location to store project/test run specific information;
|
||||
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
|
||||
in ``rootdir`` to store its cross-test run state.
|
||||
|
||||
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||
``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||
influence how modules are imported. See :ref:`pythonpath` for more details.
|
||||
|
||||
``--rootdir=path`` command-line option can be used to force a specific directory.
|
||||
The ``--rootdir=path`` command-line option can be used to force a specific directory.
|
||||
The directory passed may contain environment variables when it is used in conjunction
|
||||
with ``addopts`` in a ``pytest.ini`` file.
|
||||
|
||||
|
||||
@@ -19,6 +19,30 @@ Below is a complete list of all pytest features which are considered deprecated.
|
||||
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
|
||||
:ref:`standard warning filters <warnings>`.
|
||||
|
||||
|
||||
``--no-print-logs`` command-line option
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.4
|
||||
|
||||
|
||||
Option ``--no-print-logs`` is deprecated and meant to be removed in a future release. If you use ``--no-print-logs``, please try out ``--show-capture`` and
|
||||
provide feedback.
|
||||
|
||||
``--show-capture`` command-line option was added in ``pytest 3.5.0` and allows to specify how to
|
||||
display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default).
|
||||
|
||||
|
||||
|
||||
Node Construction changed to ``Node.from_parent``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.4
|
||||
|
||||
The construction of nodes new should use the named constructor ``from_parent``.
|
||||
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.
|
||||
|
||||
|
||||
``junit_family`` default value change to "xunit2"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -71,6 +95,18 @@ The plan is remove the ``--result-log`` option in pytest 6.0 if ``pytest-reportl
|
||||
to all users and is deemed stable. The ``pytest-reportlog`` plugin might even be merged into the core
|
||||
at some point, depending on the plans for the plugins and number of users using it.
|
||||
|
||||
TerminalReporter.writer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.4
|
||||
|
||||
The ``TerminalReporter.writer`` attribute has been deprecated and should no longer be used. This
|
||||
was inadvertently exposed as part of the public API of that plugin and ties it too much
|
||||
with ``py.io.TerminalWriter``.
|
||||
|
||||
Plugins that used ``TerminalReporter.writer`` directly should instead use ``TerminalReporter``
|
||||
methods that provide the same functionality.
|
||||
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
|
||||
@@ -19,7 +19,7 @@ Branches
|
||||
|
||||
We have two long term branches:
|
||||
|
||||
* ``master``: contains the code for the next bugfix release.
|
||||
* ``master``: contains the code for the next bug-fix release.
|
||||
* ``features``: contains the code with new features for the next minor release.
|
||||
|
||||
The official repository usually does not contain topic branches, developers and contributors should create topic
|
||||
@@ -57,4 +57,4 @@ Issues created at those events should have other relevant labels added as well.
|
||||
Those labels should be removed after they are no longer relevant.
|
||||
|
||||
|
||||
.. include:: ../../HOWTORELEASE.rst
|
||||
.. include:: ../../RELEASING.rst
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def setup(request):
|
||||
setup = CostlySetup()
|
||||
yield setup
|
||||
setup.finalize()
|
||||
|
||||
|
||||
class CostlySetup:
|
||||
def __init__(self):
|
||||
import time
|
||||
|
||||
print("performing costly setup")
|
||||
time.sleep(5)
|
||||
self.timecostly = 1
|
||||
|
||||
def finalize(self):
|
||||
del self.timecostly
|
||||
@@ -1 +0,0 @@
|
||||
#
|
||||
@@ -1,2 +0,0 @@
|
||||
def test_quick(setup):
|
||||
pass
|
||||
@@ -1 +0,0 @@
|
||||
#
|
||||
@@ -1,6 +0,0 @@
|
||||
def test_something(setup):
|
||||
assert setup.timecostly == 1
|
||||
|
||||
|
||||
def test_something_more(setup):
|
||||
assert setup.timecostly == 1
|
||||
@@ -148,6 +148,10 @@ which implements a substring match on the test names instead of the
|
||||
exact match on markers that ``-m`` provides. This makes it easy to
|
||||
select tests based on their names:
|
||||
|
||||
.. versionadded: 5.4
|
||||
|
||||
The expression matching is now case-insensitive.
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest -v -k http # running with the above defined example module
|
||||
@@ -711,6 +715,9 @@ We can now use the ``-m option`` to select one set:
|
||||
test_module.py:8: in test_interface_complex
|
||||
assert 0
|
||||
E assert 0
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_interface_simple - assert 0
|
||||
FAILED test_module.py::test_interface_complex - assert 0
|
||||
===================== 2 failed, 2 deselected in 0.12s ======================
|
||||
|
||||
or to select both "event" and "interface" tests:
|
||||
@@ -739,4 +746,8 @@ or to select both "event" and "interface" tests:
|
||||
test_module.py:12: in test_event_simple
|
||||
assert 0
|
||||
E assert 0
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_interface_simple - assert 0
|
||||
FAILED test_module.py::test_interface_complex - assert 0
|
||||
FAILED test_module.py::test_event_simple - assert 0
|
||||
===================== 3 failed, 1 deselected in 0.12s ======================
|
||||
|
||||
@@ -41,6 +41,8 @@ now execute the test specification:
|
||||
usecase execution failed
|
||||
spec failed: 'some': 'other'
|
||||
no further details known at this point.
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_simple.yaml::hello
|
||||
======================= 1 failed, 1 passed in 0.12s ========================
|
||||
|
||||
.. regendoc:wipe
|
||||
@@ -77,6 +79,8 @@ consulted when reporting in ``verbose`` mode:
|
||||
usecase execution failed
|
||||
spec failed: 'some': 'other'
|
||||
no further details known at this point.
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_simple.yaml::hello
|
||||
======================= 1 failed, 1 passed in 0.12s ========================
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
@@ -4,7 +4,7 @@ import pytest
|
||||
|
||||
def pytest_collect_file(parent, path):
|
||||
if path.ext == ".yaml" and path.basename.startswith("test"):
|
||||
return YamlFile(path, parent)
|
||||
return YamlFile.from_parent(parent, fspath=path)
|
||||
|
||||
|
||||
class YamlFile(pytest.File):
|
||||
@@ -13,7 +13,7 @@ class YamlFile(pytest.File):
|
||||
|
||||
raw = yaml.safe_load(self.fspath.open())
|
||||
for name, spec in sorted(raw.items()):
|
||||
yield YamlItem(name, self, spec)
|
||||
yield YamlItem.from_parent(self, name=name, spec=spec)
|
||||
|
||||
|
||||
class YamlItem(pytest.Item):
|
||||
|
||||
@@ -73,6 +73,8 @@ let's run the full monty:
|
||||
E assert 4 < 4
|
||||
|
||||
test_compute.py:4: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_compute.py::test_compute[4] - assert 4 < 4
|
||||
1 failed, 4 passed in 0.12s
|
||||
|
||||
As expected when running the full range of ``param1`` values
|
||||
@@ -343,6 +345,8 @@ And then when we run the test:
|
||||
E Failed: deliberately failing for demo purposes
|
||||
|
||||
test_backends.py:8: Failed
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_backends.py::test_db_initialized[d2] - Failed: deliberately f...
|
||||
1 failed, 1 passed in 0.12s
|
||||
|
||||
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
|
||||
@@ -454,6 +458,8 @@ argument sets to use for each test function. Let's run it:
|
||||
E assert 1 == 2
|
||||
|
||||
test_parametrize.py:21: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_parametrize.py::TestClass::test_equals[1-2] - assert 1 == 2
|
||||
1 failed, 2 passed in 0.12s
|
||||
|
||||
Indirect parametrization with multiple fixtures
|
||||
@@ -475,10 +481,10 @@ Running it results in some skips if we don't have all the python interpreters in
|
||||
.. code-block:: pytest
|
||||
|
||||
. $ pytest -rs -q multipython.py
|
||||
ssssssssssssssssssssssss... [100%]
|
||||
ssssssssssss...ssssssssssss [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.6' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.7' not found
|
||||
3 passed, 24 skipped in 0.12s
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
@@ -604,13 +610,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 18 items / 15 deselected / 3 selected
|
||||
collecting ... collected 14 items / 11 deselected / 3 selected
|
||||
|
||||
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]
|
||||
test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%]
|
||||
test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%]
|
||||
|
||||
=============== 2 passed, 15 deselected, 1 xfailed in 0.12s ================
|
||||
=============== 2 passed, 11 deselected, 1 xfailed in 0.12s ================
|
||||
|
||||
As the result:
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
py3 = sys.version_info[0] >= 3
|
||||
|
||||
|
||||
class DummyCollector(pytest.collect.File):
|
||||
def collect(self):
|
||||
return []
|
||||
|
||||
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
bn = path.basename
|
||||
if "py3" in bn and not py3 or ("py2" in bn and py3):
|
||||
return DummyCollector(path, parent=parent)
|
||||
@@ -1,5 +0,0 @@
|
||||
def test_exception_syntax():
|
||||
try:
|
||||
0 / 0
|
||||
except ZeroDivisionError, e:
|
||||
assert e
|
||||
@@ -1,5 +0,0 @@
|
||||
def test_exception_syntax():
|
||||
try:
|
||||
0 / 0
|
||||
except ZeroDivisionError as e:
|
||||
assert e
|
||||
@@ -81,8 +81,8 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
def test_eq_text(self):
|
||||
> assert "spam" == "eggs"
|
||||
E AssertionError: assert 'spam' == 'eggs'
|
||||
E - spam
|
||||
E + eggs
|
||||
E - eggs
|
||||
E + spam
|
||||
|
||||
failure_demo.py:45: AssertionError
|
||||
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
|
||||
@@ -92,9 +92,9 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
def test_eq_similar_text(self):
|
||||
> assert "foo 1 bar" == "foo 2 bar"
|
||||
E AssertionError: assert 'foo 1 bar' == 'foo 2 bar'
|
||||
E - foo 1 bar
|
||||
E - foo 2 bar
|
||||
E ? ^
|
||||
E + foo 2 bar
|
||||
E + foo 1 bar
|
||||
E ? ^
|
||||
|
||||
failure_demo.py:48: AssertionError
|
||||
@@ -106,8 +106,8 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
> assert "foo\nspam\nbar" == "foo\neggs\nbar"
|
||||
E AssertionError: assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
|
||||
E foo
|
||||
E - spam
|
||||
E + eggs
|
||||
E - eggs
|
||||
E + spam
|
||||
E bar
|
||||
|
||||
failure_demo.py:51: AssertionError
|
||||
@@ -122,9 +122,9 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
E AssertionError: assert '111111111111...2222222222222' == '111111111111...2222222222222'
|
||||
E Skipping 90 identical leading characters in diff, use -v to show
|
||||
E Skipping 91 identical trailing characters in diff, use -v to show
|
||||
E - 1111111111a222222222
|
||||
E - 1111111111b222222222
|
||||
E ? ^
|
||||
E + 1111111111b222222222
|
||||
E + 1111111111a222222222
|
||||
E ? ^
|
||||
|
||||
failure_demo.py:56: AssertionError
|
||||
@@ -650,4 +650,49 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
|
||||
|
||||
failure_demo.py:282: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED failure_demo.py::test_generative[3-6] - assert (3 * 2) < 6
|
||||
FAILED failure_demo.py::TestFailing::test_simple - assert 42 == 43
|
||||
FAILED failure_demo.py::TestFailing::test_simple_multiline - assert 42 == 54
|
||||
FAILED failure_demo.py::TestFailing::test_not - assert not 42
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_text - Asser...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_similar_text
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_multiline_text
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_long_text - ...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_long_text_multiline
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_list - asser...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_list_long - ...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_dict - Asser...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_set - Assert...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_longer_list
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_in_list - asser...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_not_in_text_multiline
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_not_in_text_single
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_not_in_text_single_long
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_not_in_text_single_long_term
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_dataclass - ...
|
||||
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_attrs - Asse...
|
||||
FAILED failure_demo.py::test_attribute - assert 1 == 2
|
||||
FAILED failure_demo.py::test_attribute_instance - AssertionError: assert ...
|
||||
FAILED failure_demo.py::test_attribute_failure - Exception: Failed to get...
|
||||
FAILED failure_demo.py::test_attribute_multiple - AssertionError: assert ...
|
||||
FAILED failure_demo.py::TestRaises::test_raises - ValueError: invalid lit...
|
||||
FAILED failure_demo.py::TestRaises::test_raises_doesnt - Failed: DID NOT ...
|
||||
FAILED failure_demo.py::TestRaises::test_raise - ValueError: demo error
|
||||
FAILED failure_demo.py::TestRaises::test_tupleerror - ValueError: not eno...
|
||||
FAILED failure_demo.py::TestRaises::test_reinterpret_fails_with_print_for_the_fun_of_it
|
||||
FAILED failure_demo.py::TestRaises::test_some_error - NameError: name 'na...
|
||||
FAILED failure_demo.py::test_dynamic_compile_shows_nicely - AssertionError
|
||||
FAILED failure_demo.py::TestMoreErrors::test_complex_error - assert 44 == 43
|
||||
FAILED failure_demo.py::TestMoreErrors::test_z1_unpack_error - ValueError...
|
||||
FAILED failure_demo.py::TestMoreErrors::test_z2_type_error - TypeError: c...
|
||||
FAILED failure_demo.py::TestMoreErrors::test_startswith - AssertionError:...
|
||||
FAILED failure_demo.py::TestMoreErrors::test_startswith_nested - Assertio...
|
||||
FAILED failure_demo.py::TestMoreErrors::test_global_func - assert False
|
||||
FAILED failure_demo.py::TestMoreErrors::test_instance - assert 42 != 42
|
||||
FAILED failure_demo.py::TestMoreErrors::test_compare - assert 11 < 5
|
||||
FAILED failure_demo.py::TestMoreErrors::test_try_finally - assert 1 == 0
|
||||
FAILED failure_demo.py::TestCustomAssertMsg::test_single_line - Assertion...
|
||||
FAILED failure_demo.py::TestCustomAssertMsg::test_multiline - AssertionEr...
|
||||
FAILED failure_demo.py::TestCustomAssertMsg::test_custom_repr - Assertion...
|
||||
============================ 44 failed in 0.12s ============================
|
||||
|
||||
@@ -65,6 +65,8 @@ Let's run this without supplying our new option:
|
||||
test_sample.py:6: AssertionError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
first
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_sample.py::test_answer - assert 0
|
||||
1 failed in 0.12s
|
||||
|
||||
And now with supplying a command line option:
|
||||
@@ -89,6 +91,8 @@ And now with supplying a command line option:
|
||||
test_sample.py:6: AssertionError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
second
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_sample.py::test_answer - assert 0
|
||||
1 failed in 0.12s
|
||||
|
||||
You can see that the command line option arrived in our test. This
|
||||
@@ -261,6 +265,8 @@ Let's run our little function:
|
||||
E Failed: not configured: 42
|
||||
|
||||
test_checkconfig.py:11: Failed
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_checkconfig.py::test_something - Failed: not configured: 42
|
||||
1 failed in 0.12s
|
||||
|
||||
If you only want to hide certain exceptions, you can set ``__tracebackhide__``
|
||||
@@ -442,8 +448,8 @@ Now we can profile which test functions execute the slowest:
|
||||
|
||||
========================= slowest 3 test durations =========================
|
||||
0.30s call test_some_are_slow.py::test_funcslow2
|
||||
0.21s call test_some_are_slow.py::test_funcslow1
|
||||
0.11s call test_some_are_slow.py::test_funcfast
|
||||
0.20s call test_some_are_slow.py::test_funcslow1
|
||||
0.10s call test_some_are_slow.py::test_funcfast
|
||||
============================ 3 passed in 0.12s =============================
|
||||
|
||||
incremental testing - test steps
|
||||
@@ -461,21 +467,52 @@ an ``incremental`` marker which is to be used on classes:
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
from typing import Dict, Tuple
|
||||
import pytest
|
||||
|
||||
# store history of failures per test class name and per index in parametrize (if parametrize used)
|
||||
_test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {}
|
||||
|
||||
|
||||
def pytest_runtest_makereport(item, call):
|
||||
if "incremental" in item.keywords:
|
||||
# incremental marker is used
|
||||
if call.excinfo is not None:
|
||||
parent = item.parent
|
||||
parent._previousfailed = item
|
||||
# the test has failed
|
||||
# retrieve the class name of the test
|
||||
cls_name = str(item.cls)
|
||||
# retrieve the index of the test (if parametrize is used in combination with incremental)
|
||||
parametrize_index = (
|
||||
tuple(item.callspec.indices.values())
|
||||
if hasattr(item, "callspec")
|
||||
else ()
|
||||
)
|
||||
# retrieve the name of the test function
|
||||
test_name = item.originalname or item.name
|
||||
# store in _test_failed_incremental the original name of the failed test
|
||||
_test_failed_incremental.setdefault(cls_name, {}).setdefault(
|
||||
parametrize_index, test_name
|
||||
)
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
if "incremental" in item.keywords:
|
||||
previousfailed = getattr(item.parent, "_previousfailed", None)
|
||||
if previousfailed is not None:
|
||||
pytest.xfail("previous test failed ({})".format(previousfailed.name))
|
||||
# retrieve the class name of the test
|
||||
cls_name = str(item.cls)
|
||||
# check if a previous test has failed for this class
|
||||
if cls_name in _test_failed_incremental:
|
||||
# retrieve the index of the test (if parametrize is used in combination with incremental)
|
||||
parametrize_index = (
|
||||
tuple(item.callspec.indices.values())
|
||||
if hasattr(item, "callspec")
|
||||
else ()
|
||||
)
|
||||
# retrieve the name of the first test function to fail for this class name and index
|
||||
test_name = _test_failed_incremental[cls_name].get(parametrize_index, None)
|
||||
# if name found, test has failed for the combination of class name & test name
|
||||
if test_name is not None:
|
||||
pytest.xfail("previous test failed ({})".format(test_name))
|
||||
|
||||
|
||||
These two hook implementations work together to abort incremental-marked
|
||||
tests in a class. Here is a test module example:
|
||||
@@ -641,6 +678,11 @@ We can run this:
|
||||
E assert 0
|
||||
|
||||
a/test_db2.py:2: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_step.py::TestUserHandling::test_modification - assert 0
|
||||
FAILED a/test_db.py::test_a1 - AssertionError: <conftest.DB object at 0x7...
|
||||
FAILED a/test_db2.py::test_a2 - AssertionError: <conftest.DB object at 0x...
|
||||
ERROR b/test_error.py::test_root
|
||||
============= 3 failed, 2 passed, 1 xfailed, 1 error in 0.12s ==============
|
||||
|
||||
The two test modules in the ``a`` directory see the same ``db`` fixture instance
|
||||
@@ -730,6 +772,9 @@ and run them:
|
||||
E assert 0
|
||||
|
||||
test_module.py:6: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_fail1 - assert 0
|
||||
FAILED test_module.py::test_fail2 - assert 0
|
||||
============================ 2 failed in 0.12s =============================
|
||||
|
||||
you will have a "failures" file which contains the failing test ids:
|
||||
@@ -845,6 +890,10 @@ and run it:
|
||||
E assert 0
|
||||
|
||||
test_module.py:19: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_call_fails - assert 0
|
||||
FAILED test_module.py::test_fail2 - assert 0
|
||||
ERROR test_module.py::test_setup_fails - assert 0
|
||||
======================== 2 failed, 1 error in 0.12s ========================
|
||||
|
||||
You'll see that the fixture finalizers could use the precise reporting
|
||||
|
||||
@@ -32,5 +32,3 @@ reinstall every time you want to run your tests, and is less brittle than
|
||||
mucking about with sys.path to point your tests at local code.
|
||||
|
||||
Also consider using :ref:`tox <use tox>`.
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
@@ -153,4 +153,6 @@ As of mid-2013, there shouldn't be a problem anymore when you
|
||||
use the standard setuptools (note that distribute has been merged
|
||||
back into setuptools which is now shipped directly with virtualenv).
|
||||
|
||||
.. include:: links.inc
|
||||
.. _nose: https://nose.readthedocs.io/en/latest/
|
||||
.. _pylib: https://py.readthedocs.io/en/latest/
|
||||
.. _`xUnit style setup`: xunit_setup.html
|
||||
|
||||
@@ -10,13 +10,19 @@ pytest fixtures: explicit, modular, scalable
|
||||
|
||||
|
||||
.. _`xUnit`: https://en.wikipedia.org/wiki/XUnit
|
||||
.. _`purpose of test fixtures`: https://en.wikipedia.org/wiki/Test_fixture#Software
|
||||
.. _`Software test fixtures`: https://en.wikipedia.org/wiki/Test_fixture#Software
|
||||
.. _`Dependency injection`: https://en.wikipedia.org/wiki/Dependency_injection
|
||||
|
||||
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
|
||||
functions:
|
||||
`Software test fixtures`_ initialize test functions. They provide a
|
||||
fixed baseline so that tests execute reliably and produce consistent,
|
||||
repeatable, results. Initialization may setup services, state, or
|
||||
other operating environments. These are accessed by test functions
|
||||
through arguments; for each fixture used by a test function there is
|
||||
typically a parameter (named after the fixture) in the test function's
|
||||
definition.
|
||||
|
||||
pytest fixtures offer dramatic improvements over the classic xUnit
|
||||
style of setup/teardown functions:
|
||||
|
||||
* fixtures have explicit names and are activated by declaring their use
|
||||
from test functions, modules, classes or whole projects.
|
||||
@@ -34,6 +40,74 @@ both styles, moving incrementally from classic to new style, as you
|
||||
prefer. You can also start out from existing :ref:`unittest.TestCase
|
||||
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
||||
|
||||
:ref:`Fixtures <fixtures-api>` are defined using the
|
||||
:ref:`@pytest.fixture <pytest.fixture-api>` decorator, :ref:`described
|
||||
below <funcargs>`. Pytest has useful built-in fixtures, listed here
|
||||
for reference:
|
||||
|
||||
:fixture:`capfd`
|
||||
Capture, as text, output to file descriptors ``1`` and ``2``.
|
||||
|
||||
:fixture:`capfdbinary`
|
||||
Capture, as bytes, output to file descriptors ``1`` and ``2``.
|
||||
|
||||
:fixture:`caplog`
|
||||
Control logging and access log entries.
|
||||
|
||||
:fixture:`capsys`
|
||||
Capture, as text, output to ``sys.stdout`` and ``sys.stderr``.
|
||||
|
||||
:fixture:`capsysbinary`
|
||||
Capture, as bytes, output to ``sys.stdout`` and ``sys.stderr``.
|
||||
|
||||
:fixture:`cache`
|
||||
Store and retrieve values across pytest runs.
|
||||
|
||||
:fixture:`doctest_namespace`
|
||||
Provide a dict injected into the docstests namespace.
|
||||
|
||||
:fixture:`monkeypatch`
|
||||
Temporarily modify classes, functions, dictionaries,
|
||||
``os.environ``, and other objects.
|
||||
|
||||
:fixture:`pytestconfig`
|
||||
Access to configuration values, pluginmanager and plugin hooks.
|
||||
|
||||
:fixture:`record_property`
|
||||
Add extra properties to the test.
|
||||
|
||||
:fixture:`record_testsuite_property`
|
||||
Add extra properties to the test suite.
|
||||
|
||||
:fixture:`recwarn`
|
||||
Record warnings emitted by test functions.
|
||||
|
||||
:fixture:`request`
|
||||
Provide information on the executing test function.
|
||||
|
||||
:fixture:`testdir`
|
||||
Provide a temporary test directory to aid in running, and
|
||||
testing, pytest plugins.
|
||||
|
||||
:fixture:`tmp_path`
|
||||
Provide a :class:`pathlib.Path` object to a temporary directory
|
||||
which is unique to each test function.
|
||||
|
||||
:fixture:`tmp_path_factory`
|
||||
Make session-scoped temporary directories and return
|
||||
:class:`pathlib.Path` objects.
|
||||
|
||||
:fixture:`tmpdir`
|
||||
Provide a :class:`py.path.local` object to a temporary
|
||||
directory which is unique to each test function;
|
||||
replaced by :fixture:`tmp_path`.
|
||||
|
||||
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
|
||||
|
||||
:fixture:`tmpdir_factory`
|
||||
Make session-scoped temporary directories and return
|
||||
:class:`py.path.local` objects;
|
||||
replaced by :fixture:`tmp_path_factory`.
|
||||
|
||||
.. _`funcargs`:
|
||||
.. _`funcarg mechanism`:
|
||||
@@ -96,6 +170,8 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
|
||||
E assert 0
|
||||
|
||||
test_smtpsimple.py:14: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_smtpsimple.py::test_ehlo - assert 0
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
In the failure traceback we see that the test function was called with a
|
||||
@@ -258,6 +334,9 @@ inspect what is going on and can now run the tests:
|
||||
E assert 0
|
||||
|
||||
test_module.py:13: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_ehlo - assert 0
|
||||
FAILED test_module.py::test_noop - assert 0
|
||||
============================ 2 failed in 0.12s =============================
|
||||
|
||||
You see the two ``assert 0`` failing and more importantly you can also see
|
||||
@@ -320,7 +399,7 @@ containers for different environments. See the example below.
|
||||
.. code-block:: python
|
||||
|
||||
def determine_scope(fixture_name, config):
|
||||
if config.getoption("--keep-containers"):
|
||||
if config.getoption("--keep-containers", None):
|
||||
return "session"
|
||||
return "function"
|
||||
|
||||
@@ -391,6 +470,9 @@ Let's execute it:
|
||||
$ pytest -s -q --tb=no
|
||||
FFteardown smtp
|
||||
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_ehlo - assert 0
|
||||
FAILED test_module.py::test_noop - assert 0
|
||||
2 failed in 0.12s
|
||||
|
||||
We see that the ``smtp_connection`` instance is finalized after the two
|
||||
@@ -545,6 +627,9 @@ again, nothing much has changed:
|
||||
$ pytest -s -q --tb=no
|
||||
FFfinalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
|
||||
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_ehlo - assert 0
|
||||
FAILED test_module.py::test_noop - assert 0
|
||||
2 failed in 0.12s
|
||||
|
||||
Let's quickly create another test module that actually sets the
|
||||
@@ -574,6 +659,8 @@ Running it:
|
||||
E assert 0
|
||||
------------------------- Captured stdout teardown -------------------------
|
||||
finalizing <smtplib.SMTP object at 0xdeadbeef> (mail.python.org)
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_anothersmtp.py::test_showhelo - AssertionError: (250, b'mail....
|
||||
|
||||
voila! The ``smtp_connection`` fixture function picked up our mail server name
|
||||
from the module namespace.
|
||||
@@ -722,6 +809,11 @@ So let's just do another run:
|
||||
test_module.py:13: AssertionError
|
||||
------------------------- Captured stdout teardown -------------------------
|
||||
finalizing <smtplib.SMTP object at 0xdeadbeef>
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_module.py::test_ehlo[smtp.gmail.com] - assert 0
|
||||
FAILED test_module.py::test_noop[smtp.gmail.com] - assert 0
|
||||
FAILED test_module.py::test_ehlo[mail.python.org] - AssertionError: asser...
|
||||
FAILED test_module.py::test_noop[mail.python.org] - assert 0
|
||||
4 failed in 0.12s
|
||||
|
||||
We see that our two test functions each ran twice, against the different
|
||||
@@ -849,7 +941,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:
|
||||
Modularity: using fixtures from a fixture function
|
||||
----------------------------------------------------------
|
||||
|
||||
You can not only use fixtures in test functions but fixture functions
|
||||
In addition to using fixtures in test functions, fixture functions
|
||||
can use other fixtures themselves. This contributes to a modular design
|
||||
of your fixtures and allows re-use of framework-specific fixtures across
|
||||
many projects. As a simple example, we can extend the previous example
|
||||
@@ -1035,15 +1127,21 @@ file:
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@pytest.fixture
|
||||
def cleandir():
|
||||
old_cwd = os.getcwd()
|
||||
newpath = tempfile.mkdtemp()
|
||||
os.chdir(newpath)
|
||||
yield
|
||||
os.chdir(old_cwd)
|
||||
shutil.rmtree(newpath)
|
||||
|
||||
and declare its use in a test module via a ``usefixtures`` marker:
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Install ``pytest``
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest.py
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.8/site-packages/pytest/__init__.py
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
@@ -69,6 +69,8 @@ That’s it. You can now execute the test function:
|
||||
E + where 4 = func(3)
|
||||
|
||||
test_sample.py:6: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_sample.py::test_answer - assert 4 == 5
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
This test returns a failure report because ``func(3)`` does not return ``5``.
|
||||
@@ -127,7 +129,7 @@ Once you develop multiple tests, you may want to group them into a class. pytest
|
||||
x = "hello"
|
||||
assert hasattr(x, "check")
|
||||
|
||||
``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery <test discovery>`, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename:
|
||||
``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery <test discovery>`, so it finds both ``test_`` prefixed functions. There is no need to subclass anything, but make sure to prefix your class with ``Test`` otherwise the class will be skipped. We can simply run the module by passing its filename:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
@@ -145,6 +147,8 @@ Once you develop multiple tests, you may want to group them into a class. pytest
|
||||
E + where False = hasattr('hello', 'check')
|
||||
|
||||
test_class.py:8: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_class.py::TestClass::test_two - AssertionError: assert False
|
||||
1 failed, 1 passed in 0.12s
|
||||
|
||||
The first test passed and the second failed. You can easily see the intermediate values in the assertion to help you understand the reason for the failure.
|
||||
@@ -180,6 +184,8 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look
|
||||
test_tmpdir.py:3: AssertionError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
PYTEST_TMPDIR/test_needsfiles0
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_tmpdir.py::test_needsfiles - assert 0
|
||||
1 failed in 0.12s
|
||||
|
||||
More info on tmpdir handling is available at :ref:`Temporary directories and files <tmpdir handling>`.
|
||||
@@ -203,5 +209,3 @@ Check out additional pytest resources to help you customize tests for your uniqu
|
||||
* ":ref:`fixtures`" for providing a functional baseline to your tests
|
||||
* ":ref:`plugins`" for managing and writing plugins
|
||||
* ":ref:`goodpractices`" for virtualenv and test layouts
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
@@ -232,5 +232,4 @@ options. It will run tests against the installed package and not
|
||||
against your source code checkout, helping to detect packaging
|
||||
glitches.
|
||||
|
||||
|
||||
.. include:: links.inc
|
||||
.. _`venv`: https://docs.python.org/3/library/venv.html
|
||||
|
||||
@@ -111,7 +111,7 @@ More details can be found in the `original PR <https://github.com/pytest-dev/pyt
|
||||
|
||||
.. note::
|
||||
|
||||
in a future major relase of pytest we will introduce class based markers,
|
||||
in a future major release of pytest we will introduce class based markers,
|
||||
at which point markers will no longer be limited to instances of :py:class:`Mark`.
|
||||
|
||||
|
||||
|
||||
BIN
doc/en/img/favicon.png
Normal file
BIN
doc/en/img/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -44,6 +44,8 @@ To execute it:
|
||||
E + where 4 = inc(3)
|
||||
|
||||
test_sample.py:6: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_sample.py::test_answer - assert 4 == 5
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used.
|
||||
@@ -92,7 +94,7 @@ It provide tools to raise money and share your finances in full transparency.
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
See more datails in the `pytest collective`_.
|
||||
See more details in the `pytest collective`_.
|
||||
|
||||
.. _Open Collective: https://opencollective.com
|
||||
.. _pytest collective: https://opencollective.com/pytest
|
||||
@@ -112,7 +114,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
pytest has never been associated with a security vulnerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
@@ -120,7 +122,7 @@ Tidelift will coordinate the fix and disclosure.
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2017.
|
||||
Copyright Holger Krekel and others, 2004-2020.
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Distributed under the terms of the `MIT`_ license, pytest is free and open sourc
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2019 Holger Krekel and others
|
||||
Copyright (c) 2004-2020 Holger Krekel and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
.. _`skipping plugin`: plugin/skipping.html
|
||||
.. _`funcargs mechanism`: funcargs.html
|
||||
.. _`doctest.py`: http://docs.python.org/library/doctest.html
|
||||
.. _`xUnit style setup`: xunit_setup.html
|
||||
.. _`pytest_nose`: plugin/nose.html
|
||||
.. _`reStructured Text`: http://docutils.sourceforge.net
|
||||
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
|
||||
.. _nose: https://nose.readthedocs.io/en/latest/
|
||||
.. _pytest: https://pypi.org/project/pytest/
|
||||
.. _mercurial: http://mercurial.selenic.com/wiki/
|
||||
.. _`setuptools`: https://pypi.org/project/setuptools/
|
||||
.. _`easy_install`:
|
||||
.. _`distribute docs`:
|
||||
.. _`distribute`: https://pypi.org/project/distribute/
|
||||
.. _`pip`: https://pypi.org/project/pip/
|
||||
.. _`venv`: https://docs.python.org/3/library/venv.html
|
||||
.. _`virtualenv`: https://pypi.org/project/virtualenv/
|
||||
.. _hudson: http://hudson-ci.org/
|
||||
.. _jenkins: http://jenkins-ci.org/
|
||||
.. _tox: http://testrun.org/tox
|
||||
.. _pylib: https://py.readthedocs.io/en/latest/
|
||||
@@ -3,8 +3,6 @@
|
||||
Running tests written for nose
|
||||
=======================================
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
``pytest`` has basic support for running tests written for nose_.
|
||||
|
||||
.. _nosestyle:
|
||||
@@ -72,3 +70,5 @@ Unsupported idioms / known issues
|
||||
There are no plans to fix this currently because ``yield``-tests
|
||||
are deprecated in pytest 3.0, with ``pytest.mark.parametrize``
|
||||
being the recommended alternative.
|
||||
|
||||
.. _nose: https://nose.readthedocs.io/en/latest/
|
||||
|
||||
@@ -75,6 +75,8 @@ them in turn:
|
||||
E + where 54 = eval('6*9')
|
||||
|
||||
test_expectation.py:6: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_expectation.py::test_eval[6*9-42] - AssertionError: assert 54...
|
||||
======================= 1 failed, 2 passed in 0.12s ========================
|
||||
|
||||
.. note::
|
||||
@@ -225,6 +227,8 @@ Let's also run with a stringinput that will lead to a failing test:
|
||||
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
|
||||
|
||||
test_strings.py:4: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False
|
||||
1 failed in 0.12s
|
||||
|
||||
As expected our test function fails.
|
||||
|
||||
@@ -41,8 +41,7 @@ Here is a little annotated list for some popular plugins:
|
||||
* `pytest-instafail <https://pypi.org/project/pytest-instafail/>`_:
|
||||
to report failures while the test run is happening.
|
||||
|
||||
* `pytest-bdd <https://pypi.org/project/pytest-bdd/>`_ and
|
||||
`pytest-konira <https://pypi.org/project/pytest-konira/>`_
|
||||
* `pytest-bdd <https://pypi.org/project/pytest-bdd/>`_:
|
||||
to write tests using behaviour-driven testing.
|
||||
|
||||
* `pytest-timeout <https://pypi.org/project/pytest-timeout/>`_:
|
||||
|
||||
@@ -72,7 +72,7 @@ Some organisations using pytest
|
||||
-----------------------------------
|
||||
|
||||
* `Square Kilometre Array, Cape Town <http://ska.ac.za/>`_
|
||||
* `Some Mozilla QA people <http://www.theautomatedtester.co.uk/blog/2011/pytest_and_xdist_plugin.html>`_ use pytest to distribute their Selenium tests
|
||||
* `Some Mozilla QA people <https://www.theautomatedtester.co.uk/blog/2011/pytest_and_xdist_plugin/>`_ use pytest to distribute their Selenium tests
|
||||
* `Shootq <http://web.shootq.com/>`_
|
||||
* `Stups department of Heinrich Heine University Duesseldorf <http://www.stups.uni-duesseldorf.de/projects.php>`_
|
||||
* cellzome
|
||||
|
||||
@@ -1,31 +1,97 @@
|
||||
Python 2.7 and 3.4 support plan
|
||||
===============================
|
||||
Python 2.7 and 3.4 support
|
||||
==========================
|
||||
|
||||
Python 2.7 EOL is fast approaching, with
|
||||
upstream support `ending in 2020 <https://legacy.python.org/dev/peps/pep-0373/#id4>`__.
|
||||
Python 3.4's last release is scheduled for
|
||||
`March 2019 <https://www.python.org/dev/peps/pep-0429/#release-schedule>`__. pytest is one of
|
||||
the participating projects of the https://python3statement.org.
|
||||
It is demanding on the maintainers of an open source project to support many Python versions, as
|
||||
there's extra cost of keeping code compatible between all versions, while holding back on
|
||||
features only made possible on newer Python versions.
|
||||
|
||||
The **pytest 4.6** series is the last to support Python 2.7 and 3.4, and was released in
|
||||
**June 2019**. **pytest 5.0** and onwards will support only Python 3.5+.
|
||||
In case of Python 2 and 3, the difference between the languages makes it even more prominent,
|
||||
because many new Python 3 features cannot be used in a Python 2/3 compatible code base.
|
||||
|
||||
Thanks to the `python_requires`_ ``setuptools`` option,
|
||||
Python 2.7 and Python 3.4 users using a modern ``pip`` version
|
||||
will install the last pytest ``4.6`` version automatically even if ``5.0`` or later
|
||||
Python 2.7 EOL has been reached `in 2020 <https://legacy.python.org/dev/peps/pep-0373/#id4>`__, with
|
||||
the last release planned for mid-April, 2020.
|
||||
|
||||
Python 3.4 EOL has been reached `in 2019 <https://www.python.org/dev/peps/pep-0429/#release-schedule>`__, with the last release made in March, 2019.
|
||||
|
||||
For those reasons, in Jun 2019 it was decided that **pytest 4.6** series will be the last to support Python 2.7 and 3.4.
|
||||
|
||||
What this means for general users
|
||||
---------------------------------
|
||||
|
||||
Thanks to the `python_requires`_ setuptools option,
|
||||
Python 2.7 and Python 3.4 users using a modern pip version
|
||||
will install the last pytest 4.6.X version automatically even if 5.0 or later versions
|
||||
are available on PyPI.
|
||||
|
||||
While pytest ``5.0`` will be the new mainstream and development version, until **January 2020**
|
||||
the pytest core team plans to make bug-fix releases of the pytest ``4.6`` series by
|
||||
back-porting patches to the ``4.6-maintenance`` branch that affect Python 2 users.
|
||||
Users should ensure they are using the latest pip and setuptools versions for this to work.
|
||||
|
||||
**After 2020**, the core team will no longer actively backport patches, but the ``4.6-maintenance``
|
||||
branch will continue to exist so the community itself can contribute patches. The core team will
|
||||
be happy to accept those patches and make new ``4.6`` releases **until mid-2020**.
|
||||
Maintenance of 4.6.X versions
|
||||
-----------------------------
|
||||
|
||||
Until January 2020, the pytest core team ported many bug-fixes from the main release into the
|
||||
``4.6.x`` branch, with several 4.6.X releases being made along the year.
|
||||
|
||||
From now on, the core team will **no longer actively backport patches**, but the ``4.6.x``
|
||||
branch will continue to exist so the community itself can contribute patches.
|
||||
|
||||
The core team will be happy to accept those patches, and make new 4.6.X releases **until mid-2020**
|
||||
(but consider that date as a ballpark, after that date the team might still decide to make new releases
|
||||
for critical bugs).
|
||||
|
||||
.. _`python_requires`: https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
|
||||
|
||||
Technical Aspects
|
||||
-----------------
|
||||
Technical aspects
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The technical aspects of the Python 2.7 and 3.4 support plan (such as when releases will occurr, how to backport fixes, etc) is described in issue `#5275 <https://github.com/pytest-dev/pytest/issues/5275>`__.
|
||||
(This section is a transcript from `#5275 <https://github.com/pytest-dev/pytest/issues/5275>`__).
|
||||
|
||||
In this section we describe the technical aspects of the Python 2.7 and 3.4 support plan.
|
||||
|
||||
What goes into 4.6.X releases
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
New 4.6.X releases will contain bug fixes only.
|
||||
|
||||
When will 4.6.X releases happen
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
New 4.6.X releases will happen after we have a few bugs in place to release, or if a few weeks have
|
||||
passed (say a single bug has been fixed a month after the latest 4.6.X release).
|
||||
|
||||
No hard rules here, just ballpark.
|
||||
|
||||
Who will handle applying bug fixes
|
||||
++++++++++++++++++++++++++++++++++
|
||||
|
||||
We core maintainers expect that people still using Python 2.7/3.4 and being affected by
|
||||
bugs to step up and provide patches and/or port bug fixes from the active branches.
|
||||
|
||||
We will be happy to guide users interested in doing so, so please don't hesitate to ask.
|
||||
|
||||
**Backporting changes into 4.6**
|
||||
|
||||
Please follow these instructions:
|
||||
|
||||
#. ``git fetch --all --prune``
|
||||
|
||||
#. ``git checkout origin/4.6.x -b backport-XXXX`` # use the PR number here
|
||||
|
||||
#. Locate the merge commit on the PR, in the *merged* message, for example:
|
||||
|
||||
nicoddemus merged commit 0f8b462 into pytest-dev:features
|
||||
|
||||
#. ``git cherry-pick -m1 REVISION`` # use the revision you found above (``0f8b462``).
|
||||
|
||||
#. Open a PR targeting ``4.6.x``:
|
||||
|
||||
* Prefix the message with ``[4.6]`` so it is an obvious backport
|
||||
* Delete the PR body, it usually contains a duplicate commit message.
|
||||
|
||||
**Providing new PRs to 4.6**
|
||||
|
||||
Fresh pull requests to ``4.6.x`` will be accepted provided that
|
||||
the equivalent code in the active branches does not contain that bug (for example, a bug is specific
|
||||
to Python 2 only).
|
||||
|
||||
Bug fixes that also happen in the mainstream version should be first fixed
|
||||
there, and then backported as per instructions above.
|
||||
|
||||
@@ -77,6 +77,8 @@ This is also discussed in details in :ref:`test discovery`.
|
||||
Invoking ``pytest`` versus ``python -m pytest``
|
||||
-----------------------------------------------
|
||||
|
||||
Running pytest with ``python -m pytest [...]`` instead of ``pytest [...]`` yields nearly
|
||||
equivalent behaviour, except that the former call will add the current directory to ``sys.path``.
|
||||
Running pytest with ``pytest [...]`` instead of ``python -m pytest [...]`` yields nearly
|
||||
equivalent behaviour, except that the latter will add the current directory to ``sys.path``, which
|
||||
is standard ``python`` behavior.
|
||||
|
||||
See also :ref:`cmdline`.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _`reference`:
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
@@ -124,7 +126,7 @@ Add warning filters to marked test items.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.warnings("ignore:.*usage will be deprecated.*:DeprecationWarning")
|
||||
@pytest.mark.filterwarnings("ignore:.*usage will be deprecated.*:DeprecationWarning")
|
||||
def test_foo():
|
||||
...
|
||||
|
||||
@@ -241,6 +243,8 @@ Will create and attach a :class:`Mark <_pytest.mark.structures.Mark>` object to
|
||||
mark.kwargs == {"method": "thread"}
|
||||
|
||||
|
||||
.. _`fixtures-api`:
|
||||
|
||||
Fixtures
|
||||
--------
|
||||
|
||||
@@ -271,6 +275,8 @@ Example of a fixture requiring another fixture:
|
||||
For more details, consult the full :ref:`fixtures docs <fixture>`.
|
||||
|
||||
|
||||
.. _`pytest.fixture-api`:
|
||||
|
||||
@pytest.fixture
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -278,7 +284,7 @@ For more details, consult the full :ref:`fixtures docs <fixture>`.
|
||||
:decorator:
|
||||
|
||||
|
||||
.. _`cache-api`:
|
||||
.. fixture:: config.cache
|
||||
|
||||
config.cache
|
||||
~~~~~~~~~~~~
|
||||
@@ -299,6 +305,8 @@ Under the hood, the cache plugin uses the simple
|
||||
.. automethod:: Cache.makedir
|
||||
|
||||
|
||||
.. fixture:: capsys
|
||||
|
||||
capsys
|
||||
~~~~~~
|
||||
|
||||
@@ -324,6 +332,8 @@ capsys
|
||||
:members:
|
||||
|
||||
|
||||
.. fixture:: capsysbinary
|
||||
|
||||
capsysbinary
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -344,6 +354,8 @@ capsysbinary
|
||||
assert captured.out == b"hello\n"
|
||||
|
||||
|
||||
.. fixture:: capfd
|
||||
|
||||
capfd
|
||||
~~~~~~
|
||||
|
||||
@@ -360,10 +372,12 @@ capfd
|
||||
|
||||
def test_system_echo(capfd):
|
||||
os.system('echo "hello"')
|
||||
captured = capsys.readouterr()
|
||||
captured = capfd.readouterr()
|
||||
assert captured.out == "hello\n"
|
||||
|
||||
|
||||
.. fixture:: capfdbinary
|
||||
|
||||
capfdbinary
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -384,6 +398,8 @@ capfdbinary
|
||||
assert captured.out == b"hello\n"
|
||||
|
||||
|
||||
.. fixture:: doctest_namespace
|
||||
|
||||
doctest_namespace
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -402,6 +418,8 @@ doctest_namespace
|
||||
For more details: :ref:`doctest_namespace`.
|
||||
|
||||
|
||||
.. fixture:: request
|
||||
|
||||
request
|
||||
~~~~~~~
|
||||
|
||||
@@ -413,12 +431,16 @@ The ``request`` fixture is a special fixture providing information of the reques
|
||||
:members:
|
||||
|
||||
|
||||
.. fixture:: pytestconfig
|
||||
|
||||
pytestconfig
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. autofunction:: _pytest.fixtures.pytestconfig()
|
||||
|
||||
|
||||
.. fixture:: record_property
|
||||
|
||||
record_property
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -427,6 +449,8 @@ record_property
|
||||
.. autofunction:: _pytest.junitxml.record_property()
|
||||
|
||||
|
||||
.. fixture:: record_testsuite_property
|
||||
|
||||
record_testsuite_property
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -434,6 +458,9 @@ record_testsuite_property
|
||||
|
||||
.. autofunction:: _pytest.junitxml.record_testsuite_property()
|
||||
|
||||
|
||||
.. fixture:: caplog
|
||||
|
||||
caplog
|
||||
~~~~~~
|
||||
|
||||
@@ -448,6 +475,8 @@ caplog
|
||||
:members:
|
||||
|
||||
|
||||
.. fixture:: monkeypatch
|
||||
|
||||
monkeypatch
|
||||
~~~~~~~~~~~
|
||||
|
||||
@@ -463,6 +492,9 @@ monkeypatch
|
||||
.. autoclass:: _pytest.monkeypatch.MonkeyPatch
|
||||
:members:
|
||||
|
||||
|
||||
.. fixture:: testdir
|
||||
|
||||
testdir
|
||||
~~~~~~~
|
||||
|
||||
@@ -489,6 +521,8 @@ To use it, include in your top-most ``conftest.py`` file:
|
||||
:members:
|
||||
|
||||
|
||||
.. fixture:: recwarn
|
||||
|
||||
recwarn
|
||||
~~~~~~~
|
||||
|
||||
@@ -512,6 +546,8 @@ Each recorded warning is an instance of :class:`warnings.WarningMessage`.
|
||||
differently; see :ref:`ensuring_function_triggers`.
|
||||
|
||||
|
||||
.. fixture:: tmp_path
|
||||
|
||||
tmp_path
|
||||
~~~~~~~~
|
||||
|
||||
@@ -523,6 +559,8 @@ tmp_path
|
||||
:no-auto-options:
|
||||
|
||||
|
||||
.. fixture:: tmp_path_factory
|
||||
|
||||
tmp_path_factory
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -538,6 +576,8 @@ tmp_path_factory
|
||||
.. automethod:: TempPathFactory.getbasetemp
|
||||
|
||||
|
||||
.. fixture:: tmpdir
|
||||
|
||||
tmpdir
|
||||
~~~~~~
|
||||
|
||||
@@ -549,6 +589,8 @@ tmpdir
|
||||
:no-auto-options:
|
||||
|
||||
|
||||
.. fixture:: tmpdir_factory
|
||||
|
||||
tmpdir_factory
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@@ -734,7 +776,7 @@ ExceptionInfo
|
||||
pytest.ExitCode
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.main.ExitCode
|
||||
.. autoclass:: _pytest.config.ExitCode
|
||||
:members:
|
||||
|
||||
|
||||
@@ -897,8 +939,8 @@ Can be either a ``str`` or ``Sequence[str]``.
|
||||
pytest_plugins = ("myapp.testsupport.tools", "myapp.testsupport.regression")
|
||||
|
||||
|
||||
pytest_mark
|
||||
~~~~~~~~~~~
|
||||
pytestmark
|
||||
~~~~~~~~~~
|
||||
|
||||
**Tutorial**: :ref:`scoped-marking`
|
||||
|
||||
@@ -1160,9 +1202,17 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
.. confval:: junit_logging
|
||||
|
||||
.. versionadded:: 3.5
|
||||
.. versionchanged:: 5.4
|
||||
``log``, ``all``, ``out-err`` options added.
|
||||
|
||||
Configures if stdout/stderr should be written to the JUnit XML file. Valid values are
|
||||
``system-out``, ``system-err``, and ``no`` (the default).
|
||||
Configures if captured output should be written to the JUnit XML file. Valid values are:
|
||||
|
||||
* ``log``: write only ``logging`` captured output.
|
||||
* ``system-out``: write captured ``stdout`` contents.
|
||||
* ``system-err``: write captured ``stderr`` contents.
|
||||
* ``out-err``: write both captured ``stdout`` and ``stderr`` contents.
|
||||
* ``all``: write captured ``logging``, ``stdout`` and ``stderr`` contents.
|
||||
* ``no`` (the default): no captured output is written.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
|
||||
@@ -234,11 +234,11 @@ expect a test to fail:
|
||||
def test_function():
|
||||
...
|
||||
|
||||
This test will be run but no traceback will be reported
|
||||
when it fails. Instead terminal reporting will list it in the
|
||||
"expected to fail" (``XFAIL``) or "unexpectedly passing" (``XPASS``) sections.
|
||||
This test will run but no traceback will be reported when it fails. Instead, terminal
|
||||
reporting will list it in the "expected to fail" (``XFAIL``) or "unexpectedly
|
||||
passing" (``XPASS``) sections.
|
||||
|
||||
Alternatively, you can also mark a test as ``XFAIL`` from within a test or setup function
|
||||
Alternatively, you can also mark a test as ``XFAIL`` from within the test or its setup function
|
||||
imperatively:
|
||||
|
||||
.. code-block:: python
|
||||
@@ -247,8 +247,19 @@ imperatively:
|
||||
if not valid_config():
|
||||
pytest.xfail("failing configuration (but should work)")
|
||||
|
||||
This will unconditionally make ``test_function`` ``XFAIL``. Note that no other code is executed
|
||||
after ``pytest.xfail`` call, differently from the marker. That's because it is implemented
|
||||
.. code-block:: python
|
||||
|
||||
def test_function2():
|
||||
import slow_module
|
||||
|
||||
if slow_module.slow_function():
|
||||
pytest.xfail("slow_module taking too long")
|
||||
|
||||
These two examples illustrate situations where you don't want to check for a condition
|
||||
at the module level, which is when a condition would otherwise be evaluated for marks.
|
||||
|
||||
This will make ``test_function`` ``XFAIL``. Note that no other code is executed after
|
||||
the ``pytest.xfail`` call, differently from the marker. That's because it is implemented
|
||||
internally by raising a known exception.
|
||||
|
||||
**Reference**: :ref:`pytest.mark.xfail ref`
|
||||
@@ -261,8 +272,8 @@ internally by raising a known exception.
|
||||
|
||||
|
||||
|
||||
Both ``XFAIL`` and ``XPASS`` don't fail the test suite, unless the ``strict`` keyword-only
|
||||
parameter is passed as ``True``:
|
||||
Both ``XFAIL`` and ``XPASS`` don't fail the test suite by default.
|
||||
You can change this by setting the ``strict`` keyword-only parameter to ``True``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ It provide tools to raise money and share your finances in full transparency.
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
See more datails in the `pytest collective`_.
|
||||
See more details in the `pytest collective`_.
|
||||
|
||||
|
||||
.. _Tidelift: https://tidelift.com
|
||||
|
||||
@@ -42,7 +42,7 @@ Talks and blog postings
|
||||
<https://www.youtube.com/watch?v=P-AhpukDIik>`_
|
||||
|
||||
- `3-part blog series about pytest from @pydanny alias Daniel Greenfeld (January
|
||||
2014) <http://pydanny.com/pytest-no-boilerplate-testing.html>`_
|
||||
2014) <https://daniel.roygreenfeld.com/pytest-no-boilerplate-testing.html>`_
|
||||
|
||||
- `pytest: helps you write better Django apps, Andreas Pelme, DjangoCon
|
||||
Europe 2014 <https://www.youtube.com/watch?v=aaArYVh6XSM>`_.
|
||||
|
||||
@@ -64,6 +64,8 @@ Running this would result in a passed test except for the last
|
||||
E assert 0
|
||||
|
||||
test_tmp_path.py:13: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_tmp_path.py::test_create_file - assert 0
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
.. _`tmp_path_factory example`:
|
||||
@@ -133,6 +135,8 @@ Running this would result in a passed test except for the last
|
||||
E assert 0
|
||||
|
||||
test_tmpdir.py:9: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_tmpdir.py::test_create_file - assert 0
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
.. _`tmpdir factory example`:
|
||||
|
||||
@@ -166,6 +166,9 @@ the ``self.db`` values in the traceback:
|
||||
E assert 0
|
||||
|
||||
test_unittest_db.py:13: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_unittest_db.py::MyTest::test_method1 - AssertionError: <conft...
|
||||
FAILED test_unittest_db.py::MyTest::test_method2 - AssertionError: <conft...
|
||||
============================ 2 failed in 0.12s =============================
|
||||
|
||||
This default pytest traceback shows that the two test methods
|
||||
@@ -238,17 +241,6 @@ was executed ahead of the ``test_method``.
|
||||
|
||||
.. _pdb-unittest-note:
|
||||
|
||||
.. note::
|
||||
|
||||
Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will
|
||||
disable tearDown and cleanup methods for the case that an Exception
|
||||
occurs. This allows proper post mortem debugging for all applications
|
||||
which have significant logic in their tearDown machinery. However,
|
||||
supporting this feature has the following side effect: If people
|
||||
overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
|
||||
to overwrite ``debug`` in the same way (this is also true for standard
|
||||
unittest).
|
||||
|
||||
.. note::
|
||||
|
||||
Due to architectural differences between the two frameworks, setup and
|
||||
|
||||
@@ -33,7 +33,7 @@ Running ``pytest`` can result in six different exit codes:
|
||||
:Exit code 4: pytest command line usage error
|
||||
:Exit code 5: No tests were collected
|
||||
|
||||
They are represented by the :class:`_pytest.main.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using:
|
||||
They are represented by the :class:`_pytest.config.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -94,8 +94,8 @@ Pytest supports several ways to run and select tests from the command-line.
|
||||
|
||||
pytest -k "MyClass and not method"
|
||||
|
||||
This will run tests which contain names that match the given *string expression*, which can
|
||||
include Python operators that use filenames, class names and function names as variables.
|
||||
This will run tests which contain names that match the given *string expression* (case-insensitive),
|
||||
which can include Python operators that use filenames, class names and function names as variables.
|
||||
The example above will run ``TestMyClass.test_something`` but not ``TestMyClass.test_method_simple``.
|
||||
|
||||
.. _nodeids:
|
||||
@@ -169,11 +169,11 @@ option you make sure a trace is shown.
|
||||
Detailed summary report
|
||||
-----------------------
|
||||
|
||||
|
||||
|
||||
The ``-r`` flag can be used to display a "short test summary info" at the end of the test session,
|
||||
making it easy in large test suites to get a clear picture of all failures, skips, xfails, etc.
|
||||
|
||||
It defaults to ``fE`` to list failures and errors.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
@@ -261,8 +261,12 @@ Here is the full list of available characters that can be used:
|
||||
- ``X`` - xpassed
|
||||
- ``p`` - passed
|
||||
- ``P`` - passed with output
|
||||
|
||||
Special characters for (de)selection of groups:
|
||||
|
||||
- ``a`` - all except ``pP``
|
||||
- ``A`` - all
|
||||
- ``N`` - none, this can be used to display nothing (since ``fE`` is the default)
|
||||
|
||||
More than one character can be used, so for example to only see failed and skipped tests, you can execute:
|
||||
|
||||
@@ -817,6 +821,9 @@ hook was invoked:
|
||||
E assert 0
|
||||
|
||||
test_example.py:14: AssertionError
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_example.py::test_fail - assert 0
|
||||
ERROR test_example.py::test_error - assert 0
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -827,5 +834,4 @@ hook was invoked:
|
||||
multiple calls to ``pytest.main()`` from the same process (in order to re-run
|
||||
tests, for example) is not recommended.
|
||||
|
||||
|
||||
.. include:: links.inc
|
||||
.. _jenkins: http://jenkins-ci.org/
|
||||
|
||||
@@ -64,6 +64,8 @@ them into errors:
|
||||
E UserWarning: api v1, should use functions from v2
|
||||
|
||||
test_show_warnings.py:5: UserWarning
|
||||
========================= short test summary info ==========================
|
||||
FAILED test_show_warnings.py::test_one - UserWarning: api v1, should use ...
|
||||
1 failed in 0.12s
|
||||
|
||||
The same option can be set in the ``pytest.ini`` file using the ``filterwarnings`` ini option.
|
||||
@@ -198,7 +200,7 @@ the regular expression ``".*U.*mode is deprecated"``.
|
||||
Ensuring code triggers a deprecation warning
|
||||
--------------------------------------------
|
||||
|
||||
You can also call a global helper for checking
|
||||
You can also use :func:`pytest.deprecated_call` for checking
|
||||
that a certain function call triggers a ``DeprecationWarning`` or
|
||||
``PendingDeprecationWarning``:
|
||||
|
||||
@@ -207,13 +209,18 @@ that a certain function call triggers a ``DeprecationWarning`` or
|
||||
import pytest
|
||||
|
||||
|
||||
def test_global():
|
||||
pytest.deprecated_call(myfunction, 17)
|
||||
def test_myfunction_deprecated():
|
||||
with pytest.deprecated_call():
|
||||
myfunction(17)
|
||||
|
||||
This test will fail if ``myfunction`` does not issue a deprecation warning
|
||||
when called with a ``17`` argument.
|
||||
|
||||
By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
|
||||
caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
|
||||
them. If you wish to record them in your own code, use the
|
||||
command ``warnings.simplefilter('always')``:
|
||||
caught when using :func:`pytest.warns` or :ref:`recwarn <recwarn>` because
|
||||
the default Python warnings filters hide
|
||||
them. If you wish to record them in your own code, use
|
||||
``warnings.simplefilter('always')``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -223,19 +230,13 @@ command ``warnings.simplefilter('always')``:
|
||||
|
||||
def test_deprecation(recwarn):
|
||||
warnings.simplefilter("always")
|
||||
warnings.warn("deprecated", DeprecationWarning)
|
||||
myfunction(17)
|
||||
assert len(recwarn) == 1
|
||||
assert recwarn.pop(DeprecationWarning)
|
||||
|
||||
You can also use it as a contextmanager:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_global():
|
||||
with pytest.deprecated_call():
|
||||
myobject.deprecated_method()
|
||||
|
||||
|
||||
The :ref:`recwarn <recwarn>` fixture automatically ensures to reset the warnings
|
||||
filter at the end of the test, so no global state is leaked.
|
||||
|
||||
.. _`asserting warnings`:
|
||||
|
||||
|
||||
@@ -179,11 +179,12 @@ assertion failures. This is provided by "assertion rewriting" which
|
||||
modifies the parsed AST before it gets compiled to bytecode. This is
|
||||
done via a :pep:`302` import hook which gets installed early on when
|
||||
``pytest`` starts up and will perform this rewriting when modules get
|
||||
imported. However since we do not want to test different bytecode
|
||||
then you will run in production this hook only rewrites test modules
|
||||
themselves as well as any modules which are part of plugins. Any
|
||||
other imported module will not be rewritten and normal assertion
|
||||
behaviour will happen.
|
||||
imported. However, since we do not want to test different bytecode
|
||||
from what you will run in production, this hook only rewrites test modules
|
||||
themselves (as defined by the :confval:`python_files` configuration option),
|
||||
and any modules which are part of plugins.
|
||||
Any other imported module will not be rewritten and normal assertion behaviour
|
||||
will happen.
|
||||
|
||||
If you have assertion helpers in other modules where you would need
|
||||
assertion rewriting to be enabled you need to ask ``pytest``
|
||||
@@ -441,8 +442,13 @@ additionally it is possible to copy examples for an example folder before runnin
|
||||
$REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time
|
||||
testdir.copy_example("test_example.py")
|
||||
|
||||
test_example.py::test_plugin
|
||||
$PYTHON_PREFIX/lib/python3.8/site-packages/_pytest/compat.py:333: PytestDeprecationWarning: The TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.
|
||||
See https://docs.pytest.org/en/latest/deprecations.html#terminalreporter-writer for more information.
|
||||
return getattr(object, name, default)
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/latest/warnings.html
|
||||
======================= 2 passed, 1 warning in 0.12s =======================
|
||||
====================== 2 passed, 2 warnings in 0.12s =======================
|
||||
|
||||
For more information about the result object that ``runpytest()`` returns, and
|
||||
the methods that it provides please check out the :py:class:`RunResult
|
||||
|
||||
@@ -10,14 +10,14 @@ build-backend = "setuptools.build_meta"
|
||||
[tool.towncrier]
|
||||
package = "pytest"
|
||||
package_dir = "src"
|
||||
filename = "CHANGELOG.rst"
|
||||
filename = "doc/en/changelog.rst"
|
||||
directory = "changelog/"
|
||||
title_format = "pytest {version} ({project_date})"
|
||||
template = "changelog/_template.rst"
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "removal"
|
||||
name = "Removals"
|
||||
directory = "breaking"
|
||||
name = "Breaking Changes"
|
||||
showcontent = true
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
@@ -54,3 +54,6 @@ template = "changelog/_template.rst"
|
||||
directory = "trivial"
|
||||
name = "Trivial/Internal Changes"
|
||||
showcontent = true
|
||||
|
||||
[tool.black]
|
||||
target-version = ['py35']
|
||||
|
||||
36
scripts/append_codecov_token.py
Normal file
36
scripts/append_codecov_token.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Appends the codecov token to the 'codecov.yml' file at the root of the repository.
|
||||
|
||||
This is done by CI during PRs and builds on the pytest-dev repository so we can upload coverage, at least
|
||||
until codecov grows some native integration like it has with Travis and AppVeyor.
|
||||
|
||||
See discussion in https://github.com/pytest-dev/pytest/pull/6441 for more information.
|
||||
"""
|
||||
import os.path
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
def main():
|
||||
this_dir = os.path.dirname(__file__)
|
||||
cov_file = os.path.join(this_dir, "..", "codecov.yml")
|
||||
|
||||
assert os.path.isfile(cov_file), "{cov_file} does not exist".format(
|
||||
cov_file=cov_file
|
||||
)
|
||||
|
||||
with open(cov_file, "a") as f:
|
||||
# token from: https://codecov.io/gh/pytest-dev/pytest/settings
|
||||
# use same URL to regenerate it if needed
|
||||
text = dedent(
|
||||
"""
|
||||
codecov:
|
||||
token: "1eca3b1f-31a2-4fb8-a8c3-138b441b50a7"
|
||||
"""
|
||||
)
|
||||
f.write(text)
|
||||
|
||||
print("Token updated:", cov_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -6,7 +6,13 @@ This script is meant to be executed after a successful deployment in Travis.
|
||||
Uses the following environment variables:
|
||||
|
||||
* GIT_TAG: the name of the tag of the current commit.
|
||||
* GH_RELEASE_NOTES_TOKEN: a personal access token with 'repo' permissions. It should be encrypted using:
|
||||
* GH_RELEASE_NOTES_TOKEN: a personal access token with 'repo' permissions.
|
||||
|
||||
Create one at:
|
||||
|
||||
https://github.com/settings/tokens
|
||||
|
||||
It should be encrypted using:
|
||||
|
||||
$travis encrypt GH_RELEASE_NOTES_TOKEN=<token> -r pytest-dev/pytest
|
||||
|
||||
@@ -33,7 +39,7 @@ def publish_github_release(slug, token, tag_name, body):
|
||||
|
||||
|
||||
def parse_changelog(tag_name):
|
||||
p = Path(__file__).parent.parent / "CHANGELOG.rst"
|
||||
p = Path(__file__).parent.parent / "doc/en/changelog.rst"
|
||||
changelog_lines = p.read_text(encoding="UTF-8").splitlines()
|
||||
|
||||
title_regex = re.compile(r"pytest (\d\.\d+\.\d+) \(\d{4}-\d{2}-\d{2}\)")
|
||||
@@ -55,26 +61,30 @@ def parse_changelog(tag_name):
|
||||
|
||||
|
||||
def convert_rst_to_md(text):
|
||||
return pypandoc.convert_text(text, "md", format="rst")
|
||||
return pypandoc.convert_text(
|
||||
text, "md", format="rst", extra_args=["--wrap=preserve"]
|
||||
)
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(argv) > 1:
|
||||
tag_name = argv[1]
|
||||
else:
|
||||
tag_name = os.environ.get("TRAVIS_TAG")
|
||||
tag_name = os.environ.get("GITHUB_REF")
|
||||
if not tag_name:
|
||||
print("tag_name not given and $TRAVIS_TAG not set", file=sys.stderr)
|
||||
print("tag_name not given and $GITHUB_REF not set", file=sys.stderr)
|
||||
return 1
|
||||
if tag_name.startswith("refs/tags/"):
|
||||
tag_name = tag_name[len("refs/tags/") :]
|
||||
|
||||
token = os.environ.get("GH_RELEASE_NOTES_TOKEN")
|
||||
if not token:
|
||||
print("GH_RELEASE_NOTES_TOKEN not set", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
slug = os.environ.get("TRAVIS_REPO_SLUG")
|
||||
slug = os.environ.get("GITHUB_REPOSITORY")
|
||||
if not slug:
|
||||
print("TRAVIS_REPO_SLUG not set", file=sys.stderr)
|
||||
print("GITHUB_REPOSITORY not set", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
rst_body = parse_changelog(tag_name)
|
||||
215
scripts/release-on-comment.py
Normal file
215
scripts/release-on-comment.py
Normal file
@@ -0,0 +1,215 @@
|
||||
"""
|
||||
This script is part of the pytest release process which is triggered by comments
|
||||
in issues.
|
||||
|
||||
This script is started by the `prepare_release.yml` workflow, which is triggered by two comment
|
||||
related events:
|
||||
|
||||
* https://help.github.com/en/actions/reference/events-that-trigger-workflows#issue-comment-event-issue_comment
|
||||
* https://help.github.com/en/actions/reference/events-that-trigger-workflows#issues-event-issues
|
||||
|
||||
This script receives the payload and a secrets on the command line.
|
||||
|
||||
The payload must contain a comment with a phrase matching this regular expression:
|
||||
|
||||
@pytestbot please prepare release from <branch name>
|
||||
|
||||
Then the appropriate version will be obtained based on the given branch name:
|
||||
|
||||
* a feature or bug fix release from master (based if there are features in the current changelog
|
||||
folder)
|
||||
* a bug fix from a maintenance branch
|
||||
|
||||
After that, it will create a release using the `release` tox environment, and push a new PR.
|
||||
|
||||
**Secret**: currently the secret is defined in the @pytestbot account, which the core maintainers
|
||||
have access to. There we created a new secret named `chatops` with write access to the repository.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from subprocess import check_call
|
||||
from subprocess import check_output
|
||||
from textwrap import dedent
|
||||
from typing import Dict
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from colorama import Fore
|
||||
from colorama import init
|
||||
from github3.repos import Repository
|
||||
|
||||
|
||||
class InvalidFeatureRelease(Exception):
|
||||
pass
|
||||
|
||||
|
||||
SLUG = "pytest-dev/pytest"
|
||||
|
||||
PR_BODY = """\
|
||||
Created automatically from {comment_url}.
|
||||
|
||||
Once all builds pass and it has been **approved** by one or more maintainers, the build
|
||||
can be released by pushing a tag `{version}` to this repository.
|
||||
"""
|
||||
|
||||
|
||||
def login(token: str) -> Repository:
|
||||
import github3
|
||||
|
||||
github = github3.login(token=token)
|
||||
owner, repo = SLUG.split("/")
|
||||
return github.repository(owner, repo)
|
||||
|
||||
|
||||
def get_comment_data(payload: Dict) -> str:
|
||||
if "comment" in payload:
|
||||
return payload["comment"]
|
||||
else:
|
||||
return payload["issue"]
|
||||
|
||||
|
||||
def validate_and_get_issue_comment_payload(
|
||||
issue_payload_path: Optional[Path],
|
||||
) -> Tuple[str, str]:
|
||||
payload = json.loads(issue_payload_path.read_text(encoding="UTF-8"))
|
||||
body = get_comment_data(payload)["body"]
|
||||
m = re.match(r"@pytestbot please prepare release from ([\w\-_\.]+)", body)
|
||||
if m:
|
||||
base_branch = m.group(1)
|
||||
else:
|
||||
base_branch = None
|
||||
return payload, base_branch
|
||||
|
||||
|
||||
def print_and_exit(msg) -> None:
|
||||
print(msg)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def trigger_release(payload_path: Path, token: str) -> None:
|
||||
payload, base_branch = validate_and_get_issue_comment_payload(payload_path)
|
||||
if base_branch is None:
|
||||
url = get_comment_data(payload)["html_url"]
|
||||
print_and_exit(
|
||||
f"Comment {Fore.CYAN}{url}{Fore.RESET} did not match the trigger command."
|
||||
)
|
||||
print()
|
||||
print(f"Precessing release for branch {Fore.CYAN}{base_branch}")
|
||||
|
||||
repo = login(token)
|
||||
|
||||
issue_number = payload["issue"]["number"]
|
||||
issue = repo.issue(issue_number)
|
||||
|
||||
check_call(["git", "checkout", f"origin/{base_branch}"])
|
||||
print("DEBUG:", check_output(["git", "rev-parse", "HEAD"]))
|
||||
|
||||
try:
|
||||
version = find_next_version(base_branch)
|
||||
except InvalidFeatureRelease as e:
|
||||
issue.create_comment(str(e))
|
||||
print_and_exit(f"{Fore.RED}{e}")
|
||||
|
||||
try:
|
||||
print(f"Version: {Fore.CYAN}{version}")
|
||||
|
||||
release_branch = f"release-{version}"
|
||||
|
||||
check_call(["git", "config", "user.name", "pytest bot"])
|
||||
check_call(["git", "config", "user.email", "pytestbot@gmail.com"])
|
||||
|
||||
check_call(["git", "checkout", "-b", release_branch, f"origin/{base_branch}"])
|
||||
|
||||
print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} created.")
|
||||
|
||||
check_call(
|
||||
[sys.executable, "scripts/release.py", version, "--skip-check-links"]
|
||||
)
|
||||
|
||||
oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git"
|
||||
check_call(["git", "push", oauth_url, f"HEAD:{release_branch}", "--force"])
|
||||
print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} pushed.")
|
||||
|
||||
body = PR_BODY.format(
|
||||
comment_url=get_comment_data(payload)["html_url"], version=version
|
||||
)
|
||||
pr = repo.create_pull(
|
||||
f"Prepare release {version}",
|
||||
base=base_branch,
|
||||
head=release_branch,
|
||||
body=body,
|
||||
)
|
||||
print(f"Pull request {Fore.CYAN}{pr.url}{Fore.RESET} created.")
|
||||
|
||||
comment = issue.create_comment(
|
||||
f"As requested, opened a PR for release `{version}`: #{pr.number}."
|
||||
)
|
||||
print(f"Notified in original comment {Fore.CYAN}{comment.url}{Fore.RESET}.")
|
||||
|
||||
print(f"{Fore.GREEN}Success.")
|
||||
except Exception as e:
|
||||
link = f"https://github.com/{SLUG}/actions/runs/{os.environ['GITHUB_RUN_ID']}"
|
||||
issue.create_comment(
|
||||
dedent(
|
||||
f"""
|
||||
Sorry, the request to prepare release `{version}` from {base_branch} failed with:
|
||||
|
||||
```
|
||||
{e}
|
||||
```
|
||||
|
||||
See: {link}.
|
||||
"""
|
||||
)
|
||||
)
|
||||
print_and_exit(f"{Fore.RED}{e}")
|
||||
|
||||
|
||||
def find_next_version(base_branch: str) -> str:
|
||||
output = check_output(["git", "tag"], encoding="UTF-8")
|
||||
valid_versions = []
|
||||
for v in output.splitlines():
|
||||
m = re.match(r"\d.\d.\d+$", v.strip())
|
||||
if m:
|
||||
valid_versions.append(tuple(int(x) for x in v.split(".")))
|
||||
|
||||
valid_versions.sort()
|
||||
last_version = valid_versions[-1]
|
||||
|
||||
changelog = Path("changelog")
|
||||
|
||||
features = list(changelog.glob("*.feature.rst"))
|
||||
breaking = list(changelog.glob("*.breaking.rst"))
|
||||
is_feature_release = features or breaking
|
||||
|
||||
if is_feature_release and base_branch != "master":
|
||||
msg = dedent(
|
||||
f"""
|
||||
Found features or breaking changes in `{base_branch}`, and feature releases can only be
|
||||
created from `master`.":
|
||||
"""
|
||||
)
|
||||
msg += "\n".join(f"* `{x.name}`" for x in sorted(features + breaking))
|
||||
raise InvalidFeatureRelease(msg)
|
||||
|
||||
if is_feature_release:
|
||||
return f"{last_version[0]}.{last_version[1] + 1}.0"
|
||||
else:
|
||||
return f"{last_version[0]}.{last_version[1]}.{last_version[2] + 1}"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
init(autoreset=True)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("payload")
|
||||
parser.add_argument("token")
|
||||
options = parser.parse_args()
|
||||
trigger_release(Path(options.payload), options.token)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -6,7 +6,7 @@ The pytest team is proud to announce the {version} release!
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
This release contains a number of bug fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
@@ -15,7 +15,7 @@ For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
As usual, you can upgrade from PyPI via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
@@ -24,4 +24,4 @@ Thanks to all who contributed to this release, among them:
|
||||
{contributors}
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
The pytest Development Team
|
||||
|
||||
@@ -85,13 +85,14 @@ def check_links():
|
||||
check_call(["tox", "-e", "docs-checklinks"])
|
||||
|
||||
|
||||
def pre_release(version):
|
||||
def pre_release(version, *, skip_check_links):
|
||||
"""Generates new docs, release announcements and creates a local tag."""
|
||||
announce(version)
|
||||
regen()
|
||||
changelog(version, write_out=True)
|
||||
fix_formatting()
|
||||
check_links()
|
||||
if not skip_check_links:
|
||||
check_links()
|
||||
|
||||
msg = "Preparing release version {}".format(version)
|
||||
check_call(["git", "commit", "-a", "-m", msg])
|
||||
@@ -99,7 +100,7 @@ def pre_release(version):
|
||||
print()
|
||||
print(f"{Fore.CYAN}[generate.pre_release] {Fore.GREEN}All done!")
|
||||
print()
|
||||
print(f"Please push your branch and open a PR.")
|
||||
print("Please push your branch and open a PR.")
|
||||
|
||||
|
||||
def changelog(version, write_out=False):
|
||||
@@ -114,8 +115,9 @@ def main():
|
||||
init(autoreset=True)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("version", help="Release version")
|
||||
parser.add_argument("--skip-check-links", action="store_true", default=False)
|
||||
options = parser.parse_args()
|
||||
pre_release(options.version)
|
||||
pre_release(options.version, skip_check_links=options.skip_check_links)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -15,4 +15,4 @@ python -m coverage xml
|
||||
python -m coverage report -m
|
||||
# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461
|
||||
curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh
|
||||
bash codecov-upload.sh -Z -X fix -f coverage.xml
|
||||
bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
@echo off
|
||||
rem Source: https://github.com/appveyor/ci/blob/master/scripts/appveyor-retry.cmd
|
||||
rem initiate the retry number
|
||||
set retryNumber=0
|
||||
set maxRetries=3
|
||||
|
||||
:RUN
|
||||
%*
|
||||
set LastErrorLevel=%ERRORLEVEL%
|
||||
IF %LastErrorLevel% == 0 GOTO :EOF
|
||||
set /a retryNumber=%retryNumber%+1
|
||||
IF %reTryNumber% == %maxRetries% (GOTO :FAILED)
|
||||
|
||||
:RETRY
|
||||
set /a retryNumberDisp=%retryNumber%+1
|
||||
@echo Command "%*" failed with exit code %LastErrorLevel%. Retrying %retryNumberDisp% of %maxRetries%
|
||||
GOTO :RUN
|
||||
|
||||
: FAILED
|
||||
@echo Sorry, we tried running command for %maxRetries% times and all attempts were unsuccessful!
|
||||
EXIT /B %LastErrorLevel%
|
||||
@@ -10,7 +10,6 @@ project_urls =
|
||||
author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
|
||||
|
||||
license = MIT license
|
||||
license_file = LICENSE
|
||||
keywords = test, unittest
|
||||
classifiers =
|
||||
Development Status :: 6 - Mature
|
||||
@@ -27,6 +26,7 @@ classifiers =
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
platforms = unix, linux, osx, cygwin, win32
|
||||
|
||||
[options]
|
||||
@@ -38,8 +38,8 @@ packages =
|
||||
_pytest.assertion
|
||||
_pytest.config
|
||||
_pytest.mark
|
||||
pytest
|
||||
|
||||
py_modules = pytest
|
||||
python_requires = >=3.5
|
||||
|
||||
[options.entry_points]
|
||||
@@ -66,6 +66,7 @@ formats = sdist.tgz,bdist_wheel
|
||||
mypy_path = src
|
||||
ignore_missing_imports = True
|
||||
no_implicit_optional = True
|
||||
show_error_codes = True
|
||||
strict_equality = True
|
||||
warn_redundant_casts = True
|
||||
warn_return_any = True
|
||||
|
||||
5
setup.py
5
setup.py
@@ -29,7 +29,10 @@ def main():
|
||||
"nose",
|
||||
"requests",
|
||||
"xmlschema",
|
||||
]
|
||||
],
|
||||
"checkqa-mypy": [
|
||||
"mypy==v0.761", # keep this in sync with .pre-commit-config.yaml.
|
||||
],
|
||||
},
|
||||
install_requires=INSTALL_REQUIRES,
|
||||
)
|
||||
|
||||
@@ -53,19 +53,22 @@ If things do not work right away:
|
||||
which should throw a KeyError: 'COMPLINE' (which is properly set by the
|
||||
global argcomplete script).
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from glob import glob
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class FastFilesCompleter:
|
||||
"Fast file completer class"
|
||||
|
||||
def __init__(self, directories=True):
|
||||
def __init__(self, directories: bool = True) -> None:
|
||||
self.directories = directories
|
||||
|
||||
def __call__(self, prefix, **kwargs):
|
||||
def __call__(self, prefix: str, **kwargs: Any) -> List[str]:
|
||||
"""only called on non option completions"""
|
||||
if os.path.sep in prefix[1:]:
|
||||
prefix_dir = len(os.path.dirname(prefix) + os.path.sep)
|
||||
@@ -94,13 +97,13 @@ if os.environ.get("_ARGCOMPLETE"):
|
||||
sys.exit(-1)
|
||||
filescompleter = FastFilesCompleter() # type: Optional[FastFilesCompleter]
|
||||
|
||||
def try_argcomplete(parser):
|
||||
def try_argcomplete(parser: argparse.ArgumentParser) -> None:
|
||||
argcomplete.autocomplete(parser, always_complete_options=False)
|
||||
|
||||
|
||||
else:
|
||||
|
||||
def try_argcomplete(parser):
|
||||
def try_argcomplete(parser: argparse.ArgumentParser) -> None:
|
||||
pass
|
||||
|
||||
filescompleter = None
|
||||
|
||||
@@ -29,18 +29,21 @@ import pluggy
|
||||
import py
|
||||
|
||||
import _pytest
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest._io.saferepr import safeformat
|
||||
from _pytest._io.saferepr import saferepr
|
||||
from _pytest.compat import ATTRS_EQ_FIELD
|
||||
from _pytest.compat import overload
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
from typing_extensions import Literal
|
||||
from weakref import ReferenceType # noqa: F401
|
||||
|
||||
from _pytest._code import Source
|
||||
|
||||
_TracebackStyle = Literal["long", "short", "no", "native"]
|
||||
_TracebackStyle = Literal["long", "short", "line", "no", "native"]
|
||||
|
||||
|
||||
class Code:
|
||||
@@ -66,20 +69,22 @@ class Code:
|
||||
return not self == other
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
""" return a path object pointing to source code (note that it
|
||||
might not point to an actually existing file). """
|
||||
def path(self) -> Union[py.path.local, str]:
|
||||
""" return a path object pointing to source code (or a str in case
|
||||
of OSError / non-existing file).
|
||||
"""
|
||||
if not self.raw.co_filename:
|
||||
return ""
|
||||
try:
|
||||
p = py.path.local(self.raw.co_filename)
|
||||
# maybe don't try this checking
|
||||
if not p.check():
|
||||
raise OSError("py.path check failed.")
|
||||
return p
|
||||
except OSError:
|
||||
# XXX maybe try harder like the weird logic
|
||||
# in the standard lib [linecache.updatecache] does?
|
||||
p = self.raw.co_filename
|
||||
|
||||
return p
|
||||
return self.raw.co_filename
|
||||
|
||||
@property
|
||||
def fullsource(self) -> Optional["Source"]:
|
||||
@@ -334,7 +339,7 @@ class Traceback(List[TracebackEntry]):
|
||||
(path is None or codepath == path)
|
||||
and (
|
||||
excludepath is None
|
||||
or not hasattr(codepath, "relto")
|
||||
or not isinstance(codepath, py.path.local)
|
||||
or not codepath.relto(excludepath)
|
||||
)
|
||||
and (lineno is None or x.lineno == lineno)
|
||||
@@ -479,7 +484,7 @@ class ExceptionInfo(Generic[_E]):
|
||||
assert tup[1] is not None, "no current exception"
|
||||
assert tup[2] is not None, "no current exception"
|
||||
exc_info = (tup[0], tup[1], tup[2])
|
||||
return cls.from_exc_info(exc_info, exprinfo)
|
||||
return ExceptionInfo.from_exc_info(exc_info, exprinfo)
|
||||
|
||||
@classmethod
|
||||
def for_later(cls) -> "ExceptionInfo[_E]":
|
||||
@@ -629,17 +634,18 @@ class ExceptionInfo(Generic[_E]):
|
||||
)
|
||||
return fmt.repr_excinfo(self)
|
||||
|
||||
def match(self, regexp: "Union[str, Pattern]") -> bool:
|
||||
def match(self, regexp: "Union[str, Pattern]") -> "Literal[True]":
|
||||
"""
|
||||
Check whether the regular expression 'regexp' is found in the string
|
||||
representation of the exception using ``re.search``. If it matches
|
||||
then True is returned (so that it is possible to write
|
||||
``assert excinfo.match()``). If it doesn't match an AssertionError is
|
||||
raised.
|
||||
Check whether the regular expression `regexp` matches the string
|
||||
representation of the exception using :func:`python:re.search`.
|
||||
If it matches `True` is returned.
|
||||
If it doesn't match an `AssertionError` is raised.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
if not re.search(regexp, str(self.value)):
|
||||
assert 0, "Pattern {!r} not found in {!r}".format(regexp, str(self.value))
|
||||
assert re.search(
|
||||
regexp, str(self.value)
|
||||
), "Pattern {!r} does not match {!r}".format(regexp, str(self.value))
|
||||
# Return True to allow for "assert excinfo.match()".
|
||||
return True
|
||||
|
||||
|
||||
@@ -784,11 +790,9 @@ class FormattedExcinfo:
|
||||
else:
|
||||
message = excinfo and excinfo.typename or ""
|
||||
path = self._makepath(entry.path)
|
||||
filelocrepr = ReprFileLocation(path, entry.lineno + 1, message)
|
||||
localsrepr = None
|
||||
if not short:
|
||||
localsrepr = self.repr_locals(entry.locals)
|
||||
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
|
||||
reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
|
||||
localsrepr = self.repr_locals(entry.locals)
|
||||
return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
|
||||
if excinfo:
|
||||
lines.extend(self.get_exconly(excinfo, indent=4))
|
||||
return ReprEntry(lines, None, None, None, style)
|
||||
@@ -908,50 +912,53 @@ class FormattedExcinfo:
|
||||
return ExceptionChainRepr(repr_chain)
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class TerminalRepr:
|
||||
def __str__(self) -> str:
|
||||
# FYI this is called from pytest-xdist's serialization of exception
|
||||
# information.
|
||||
io = StringIO()
|
||||
tw = py.io.TerminalWriter(file=io)
|
||||
tw = TerminalWriter(file=io)
|
||||
self.toterminal(tw)
|
||||
return io.getvalue().strip()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<{} instance at {:0x}>".format(self.__class__, id(self))
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ExceptionRepr(TerminalRepr):
|
||||
def __init__(self) -> None:
|
||||
def __attrs_post_init__(self):
|
||||
self.sections = [] # type: List[Tuple[str, str, str]]
|
||||
|
||||
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
||||
self.sections.append((name, content, sep))
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
for name, content, sep in self.sections:
|
||||
tw.sep(sep, name)
|
||||
tw.line(content)
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ExceptionChainRepr(ExceptionRepr):
|
||||
def __init__(
|
||||
self,
|
||||
chain: Sequence[
|
||||
chain = attr.ib(
|
||||
type=Sequence[
|
||||
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
|
||||
],
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.chain = chain
|
||||
]
|
||||
)
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
super().__attrs_post_init__()
|
||||
# reprcrash and reprtraceback of the outermost (the newest) exception
|
||||
# in the chain
|
||||
self.reprtraceback = chain[-1][0]
|
||||
self.reprcrash = chain[-1][1]
|
||||
self.reprtraceback = self.chain[-1][0]
|
||||
self.reprcrash = self.chain[-1][1]
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
for element in self.chain:
|
||||
element[0].toterminal(tw)
|
||||
if element[2] is not None:
|
||||
@@ -960,33 +967,25 @@ class ExceptionChainRepr(ExceptionRepr):
|
||||
super().toterminal(tw)
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprExceptionInfo(ExceptionRepr):
|
||||
def __init__(
|
||||
self, reprtraceback: "ReprTraceback", reprcrash: "ReprFileLocation"
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.reprtraceback = reprtraceback
|
||||
self.reprcrash = reprcrash
|
||||
reprtraceback = attr.ib(type="ReprTraceback")
|
||||
reprcrash = attr.ib(type="ReprFileLocation")
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
self.reprtraceback.toterminal(tw)
|
||||
super().toterminal(tw)
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprTraceback(TerminalRepr):
|
||||
reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]])
|
||||
extraline = attr.ib(type=Optional[str])
|
||||
style = attr.ib(type="_TracebackStyle")
|
||||
|
||||
entrysep = "_ "
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]],
|
||||
extraline: Optional[str],
|
||||
style: "_TracebackStyle",
|
||||
) -> None:
|
||||
self.reprentries = reprentries
|
||||
self.extraline = extraline
|
||||
self.style = style
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
# the entries might have different styles
|
||||
for i, entry in enumerate(self.reprentries):
|
||||
if entry.style == "long":
|
||||
@@ -1012,44 +1011,75 @@ class ReprTracebackNative(ReprTraceback):
|
||||
self.extraline = None
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprEntryNative(TerminalRepr):
|
||||
lines = attr.ib(type=Sequence[str])
|
||||
style = "native" # type: _TracebackStyle
|
||||
|
||||
def __init__(self, tblines: Sequence[str]) -> None:
|
||||
self.lines = tblines
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
tw.write("".join(self.lines))
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprEntry(TerminalRepr):
|
||||
def __init__(
|
||||
self,
|
||||
lines: Sequence[str],
|
||||
reprfuncargs: Optional["ReprFuncArgs"],
|
||||
reprlocals: Optional["ReprLocals"],
|
||||
filelocrepr: Optional["ReprFileLocation"],
|
||||
style: "_TracebackStyle",
|
||||
) -> None:
|
||||
self.lines = lines
|
||||
self.reprfuncargs = reprfuncargs
|
||||
self.reprlocals = reprlocals
|
||||
self.reprfileloc = filelocrepr
|
||||
self.style = style
|
||||
lines = attr.ib(type=Sequence[str])
|
||||
reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"])
|
||||
reprlocals = attr.ib(type=Optional["ReprLocals"])
|
||||
reprfileloc = attr.ib(type=Optional["ReprFileLocation"])
|
||||
style = attr.ib(type="_TracebackStyle")
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def _write_entry_lines(self, tw: TerminalWriter) -> None:
|
||||
"""Writes the source code portions of a list of traceback entries with syntax highlighting.
|
||||
|
||||
Usually entries are lines like these:
|
||||
|
||||
" x = 1"
|
||||
"> assert x == 2"
|
||||
"E assert 1 == 2"
|
||||
|
||||
This function takes care of rendering the "source" portions of it (the lines without
|
||||
the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
|
||||
character, as doing so might break line continuations.
|
||||
"""
|
||||
|
||||
indent_size = 4
|
||||
|
||||
def is_fail(line):
|
||||
return line.startswith("{} ".format(FormattedExcinfo.fail_marker))
|
||||
|
||||
if not self.lines:
|
||||
return
|
||||
|
||||
# separate indents and source lines that are not failures: we want to
|
||||
# highlight the code but not the indentation, which may contain markers
|
||||
# such as "> assert 0"
|
||||
indents = []
|
||||
source_lines = []
|
||||
for line in self.lines:
|
||||
if not is_fail(line):
|
||||
indents.append(line[:indent_size])
|
||||
source_lines.append(line[indent_size:])
|
||||
|
||||
tw._write_source(source_lines, indents)
|
||||
|
||||
# failure lines are always completely red and bold
|
||||
for line in (x for x in self.lines if is_fail(x)):
|
||||
tw.line(line, bold=True, red=True)
|
||||
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
if self.style == "short":
|
||||
assert self.reprfileloc is not None
|
||||
self.reprfileloc.toterminal(tw)
|
||||
for line in self.lines:
|
||||
red = line.startswith("E ")
|
||||
tw.line(line, bold=True, red=red)
|
||||
self._write_entry_lines(tw)
|
||||
if self.reprlocals:
|
||||
self.reprlocals.toterminal(tw, indent=" " * 8)
|
||||
return
|
||||
|
||||
if self.reprfuncargs:
|
||||
self.reprfuncargs.toterminal(tw)
|
||||
for line in self.lines:
|
||||
red = line.startswith("E ")
|
||||
tw.line(line, bold=True, red=red)
|
||||
|
||||
self._write_entry_lines(tw)
|
||||
|
||||
if self.reprlocals:
|
||||
tw.line("")
|
||||
self.reprlocals.toterminal(tw)
|
||||
@@ -1064,13 +1094,13 @@ class ReprEntry(TerminalRepr):
|
||||
)
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprFileLocation(TerminalRepr):
|
||||
def __init__(self, path, lineno: int, message: str) -> None:
|
||||
self.path = str(path)
|
||||
self.lineno = lineno
|
||||
self.message = message
|
||||
path = attr.ib(type=str, converter=str)
|
||||
lineno = attr.ib(type=int)
|
||||
message = attr.ib(type=str)
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
# filename and lineno output for each entry,
|
||||
# using an output format that most editors understand
|
||||
msg = self.message
|
||||
@@ -1081,20 +1111,20 @@ class ReprFileLocation(TerminalRepr):
|
||||
tw.line(":{}: {}".format(self.lineno, msg))
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprLocals(TerminalRepr):
|
||||
def __init__(self, lines: Sequence[str]) -> None:
|
||||
self.lines = lines
|
||||
lines = attr.ib(type=Sequence[str])
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter, indent="") -> None:
|
||||
for line in self.lines:
|
||||
tw.line(line)
|
||||
tw.line(indent + line)
|
||||
|
||||
|
||||
@attr.s(**{ATTRS_EQ_FIELD: False}) # type: ignore
|
||||
class ReprFuncArgs(TerminalRepr):
|
||||
def __init__(self, args: Sequence[Tuple[str, object]]) -> None:
|
||||
self.args = args
|
||||
args = attr.ib(type=Sequence[Tuple[str, object]])
|
||||
|
||||
def toterminal(self, tw) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
if self.args:
|
||||
linesofar = ""
|
||||
for name, value in self.args:
|
||||
|
||||
@@ -5,9 +5,10 @@ import sys
|
||||
import textwrap
|
||||
import tokenize
|
||||
import warnings
|
||||
from ast import PyCF_ONLY_AST as _AST_FLAG
|
||||
from bisect import bisect_right
|
||||
from types import CodeType
|
||||
from types import FrameType
|
||||
from typing import Any
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
@@ -17,7 +18,12 @@ from typing import Union
|
||||
|
||||
import py
|
||||
|
||||
from _pytest.compat import get_real_func
|
||||
from _pytest.compat import overload
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Literal
|
||||
|
||||
|
||||
class Source:
|
||||
@@ -121,7 +127,7 @@ class Source:
|
||||
start, end = self.getstatementrange(lineno)
|
||||
return self[start:end]
|
||||
|
||||
def getstatementrange(self, lineno: int):
|
||||
def getstatementrange(self, lineno: int) -> Tuple[int, int]:
|
||||
""" return (start, end) tuple which spans the minimal
|
||||
statement region which containing the given lineno.
|
||||
"""
|
||||
@@ -140,18 +146,13 @@ class Source:
|
||||
""" return True if source is parseable, heuristically
|
||||
deindenting it by default.
|
||||
"""
|
||||
from parser import suite as syntax_checker
|
||||
|
||||
if deindent:
|
||||
source = str(self.deindent())
|
||||
else:
|
||||
source = str(self)
|
||||
try:
|
||||
# compile(source+'\n', "x", "exec")
|
||||
syntax_checker(source + "\n")
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except Exception:
|
||||
ast.parse(source)
|
||||
except (SyntaxError, ValueError, TypeError):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -159,14 +160,36 @@ class Source:
|
||||
def __str__(self) -> str:
|
||||
return "\n".join(self.lines)
|
||||
|
||||
@overload
|
||||
def compile(
|
||||
self,
|
||||
filename=None,
|
||||
mode="exec",
|
||||
filename: Optional[str] = ...,
|
||||
mode: str = ...,
|
||||
flag: "Literal[0]" = ...,
|
||||
dont_inherit: int = ...,
|
||||
_genframe: Optional[FrameType] = ...,
|
||||
) -> CodeType:
|
||||
raise NotImplementedError()
|
||||
|
||||
@overload # noqa: F811
|
||||
def compile( # noqa: F811
|
||||
self,
|
||||
filename: Optional[str] = ...,
|
||||
mode: str = ...,
|
||||
flag: int = ...,
|
||||
dont_inherit: int = ...,
|
||||
_genframe: Optional[FrameType] = ...,
|
||||
) -> Union[CodeType, ast.AST]:
|
||||
raise NotImplementedError()
|
||||
|
||||
def compile( # noqa: F811
|
||||
self,
|
||||
filename: Optional[str] = None,
|
||||
mode: str = "exec",
|
||||
flag: int = 0,
|
||||
dont_inherit: int = 0,
|
||||
_genframe: Optional[FrameType] = None,
|
||||
):
|
||||
) -> Union[CodeType, ast.AST]:
|
||||
""" return compiled code object. if filename is None
|
||||
invent an artificial filename which displays
|
||||
the source/line position of the caller frame.
|
||||
@@ -196,8 +219,10 @@ class Source:
|
||||
newex.text = ex.text
|
||||
raise newex
|
||||
else:
|
||||
if flag & _AST_FLAG:
|
||||
if flag & ast.PyCF_ONLY_AST:
|
||||
assert isinstance(co, ast.AST)
|
||||
return co
|
||||
assert isinstance(co, CodeType)
|
||||
lines = [(x + "\n") for x in self.lines]
|
||||
# Type ignored because linecache.cache is private.
|
||||
linecache.cache[filename] = (1, None, lines, filename) # type: ignore
|
||||
@@ -209,7 +234,35 @@ class Source:
|
||||
#
|
||||
|
||||
|
||||
def compile_(source, filename=None, mode="exec", flags: int = 0, dont_inherit: int = 0):
|
||||
@overload
|
||||
def compile_(
|
||||
source: Union[str, bytes, ast.mod, ast.AST],
|
||||
filename: Optional[str] = ...,
|
||||
mode: str = ...,
|
||||
flags: "Literal[0]" = ...,
|
||||
dont_inherit: int = ...,
|
||||
) -> CodeType:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@overload # noqa: F811
|
||||
def compile_( # noqa: F811
|
||||
source: Union[str, bytes, ast.mod, ast.AST],
|
||||
filename: Optional[str] = ...,
|
||||
mode: str = ...,
|
||||
flags: int = ...,
|
||||
dont_inherit: int = ...,
|
||||
) -> Union[CodeType, ast.AST]:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def compile_( # noqa: F811
|
||||
source: Union[str, bytes, ast.mod, ast.AST],
|
||||
filename: Optional[str] = None,
|
||||
mode: str = "exec",
|
||||
flags: int = 0,
|
||||
dont_inherit: int = 0,
|
||||
) -> Union[CodeType, ast.AST]:
|
||||
""" compile the given source to a raw code object,
|
||||
and maintain an internal cache which allows later
|
||||
retrieval of the source code for the code object
|
||||
@@ -217,14 +270,16 @@ def compile_(source, filename=None, mode="exec", flags: int = 0, dont_inherit: i
|
||||
"""
|
||||
if isinstance(source, ast.AST):
|
||||
# XXX should Source support having AST?
|
||||
return compile(source, filename, mode, flags, dont_inherit)
|
||||
assert filename is not None
|
||||
co = compile(source, filename, mode, flags, dont_inherit)
|
||||
assert isinstance(co, (CodeType, ast.AST))
|
||||
return co
|
||||
_genframe = sys._getframe(1) # the caller
|
||||
s = Source(source)
|
||||
co = s.compile(filename, mode, flags, _genframe=_genframe)
|
||||
return co
|
||||
return s.compile(filename, mode, flags, _genframe=_genframe)
|
||||
|
||||
|
||||
def getfslineno(obj):
|
||||
def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
|
||||
""" Return source location (path, lineno) for the given object.
|
||||
If the source cannot be determined return ("", -1).
|
||||
|
||||
@@ -232,6 +287,13 @@ def getfslineno(obj):
|
||||
"""
|
||||
from .code import Code
|
||||
|
||||
# xxx let decorators etc specify a sane ordering
|
||||
# NOTE: this used to be done in _pytest.compat.getfslineno, initially added
|
||||
# in 6ec13a2b9. It ("place_as") appears to be something very custom.
|
||||
obj = get_real_func(obj)
|
||||
if hasattr(obj, "place_as"):
|
||||
obj = obj.place_as
|
||||
|
||||
try:
|
||||
code = Code(obj)
|
||||
except TypeError:
|
||||
@@ -240,18 +302,16 @@ def getfslineno(obj):
|
||||
except TypeError:
|
||||
return "", -1
|
||||
|
||||
fspath = fn and py.path.local(fn) or None
|
||||
fspath = fn and py.path.local(fn) or ""
|
||||
lineno = -1
|
||||
if fspath:
|
||||
try:
|
||||
_, lineno = findsource(obj)
|
||||
except IOError:
|
||||
pass
|
||||
return fspath, lineno
|
||||
else:
|
||||
fspath = code.path
|
||||
lineno = code.firstlineno
|
||||
assert isinstance(lineno, int)
|
||||
return fspath, lineno
|
||||
return code.path, code.firstlineno
|
||||
|
||||
|
||||
#
|
||||
@@ -321,7 +381,7 @@ def getstatementrange_ast(
|
||||
# don't produce duplicate warnings when compiling source to find ast
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
astnode = compile(content, "source", "exec", _AST_FLAG)
|
||||
astnode = ast.parse(content, "source", "exec")
|
||||
|
||||
start, end = get_statement_startend2(lineno, astnode)
|
||||
# we need to correct the end:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user