├── executing ├── py.typed ├── _pytest_utils.py ├── _exceptions.py ├── __init__.py └── _utils.py ├── tests ├── __init__.py ├── not_code.txt ├── sample_results │ ├── _parser-py-2.7.json │ ├── _parser-py-3.5.json │ ├── _parser-py-3.6.json │ ├── _parser-py-3.7.json │ ├── _parser-pypy-2.7.json │ ├── _parser-pypy-3.5.json │ ├── _parser-pypy-3.6.json │ ├── datetime-py-2.7.json │ ├── datetime-py-3.5.json │ ├── datetime-py-3.6.json │ ├── datetime-py-3.7.json │ ├── datetime-pypy-2.7.json │ ├── datetime-pypy-3.5.json │ └── datetime-pypy-3.6.json ├── small_samples │ ├── 0800ffcd43ac2c29114cbd591705492110e832d63345d278bbec93a0fc3c64e1.py │ ├── 7ec5eed65083de4adc88655f15d02ce24303aa1f2860762d800d7e9093c6eb59.py │ ├── 126f1cda2062caf2c74dcdcd77f5cb3a0e2d20b749329f84f48aa4d90701f2d0.py │ ├── ae9b3821230822abe9910bb1ebfe74ff2cedc19f646975fb2931f4b67fd4f189.py │ ├── 1a1292769ffae5636aa864bec0bf01dc60380a867a7f5b8ab6f18b523848bb39.py │ ├── 1afa3b7586b5527d711d8881bc2de395ebb8036a6c91a0cd952f02f280c83a4f.py │ ├── 28dde5e66130457326139e9f4ae62987606de2b6362b073ae3671ad38cf37046.py │ ├── 393abb63bcdcf6c3f6b008fb7a7635ac6f615f1ef83579b2ad0e735d738e9ddd.py │ ├── 867a476c53701b28e25aa4b445e586b8a6764f9f8582b98955c4352b0d8ba415.py │ ├── ad8aa993e6ee4eb5ee764d55f2e3fd636a99b2ecb8c5aff2b35fbb78a074ea30.py │ ├── dc592fb930b28fe2b7181bec5d6f4b871bdd55c01bc22b0623e610eec70df7ab.py │ ├── 0f7396913445f02c6720c718315895cdb6a259875f6b6fd29259a96c7e9c1ff5.py │ ├── 83f4e6cc115c0532d42ca1d428a10c4dd4ee966674cc7bbc7e4d53e784e6f4ff.py │ ├── a2389f211aec4c553b1cec683b416480636d7c13d10e8db50b5da567192ce42f.py │ ├── b9fe280619199e07642f4e0d263716394022a3311441d703bf943c3a5115ab40.py │ ├── e6e45407a25ab0cc42cf0eb707eacccd2d27a0aa5a00bd9bc6df69414f952c42.py │ ├── e721e8b2c275d578d37aa986c1de68f79a266f8950ce2a74604630d4b0387d85.py │ ├── 09c2b2709b3507baf8a9e749c6e3bed21728d2159858adcfa66c49bb3e2f35f2.py │ ├── 2b4b06fbfb9588ba2f85687c472bd0a2b74bf299ad3591b3957eb99dfb9342fb.py │ ├── 8519155d8a424c7cbc4bc15042d50c3193688c600ac9552f9503672e7c01b4d9.py │ ├── 8d7d8e2330522993cf517ba2f4191e01c336fb27bbbfa40815629432b96d74fa.py │ ├── b8be3216d583e1ce26486ee2aff5cbe74fed88ebba7118db80a375c9514d1bc1.py │ ├── f09192915e250c0e1630b5d9add1328874fcb799cc508db1d7b6a880b2d0acea.py │ ├── 1fff64406be948c8ed049b0fc299b0e3702064f8592d6a020136cc0485e348cf.py │ ├── 3d740a1da7646802a0c11ca94604e6da910ef47148032a1ec0a50047b9fcc024.py │ ├── 495122dc195a96d0c32cddce6153504521b6bea10cbe5faa1313062c57f198a0.py │ ├── 524a7a805db753f5ea998182ddaea49a177b75a7ae88ab77eaa879755857a15a.py │ ├── a35a3b669faf7fa486c7ca0a95a61efab9591a0d1677406c48c53e1a04ebe850.py │ ├── a8c5d8fec98be9e324c50d6c89b207e35f3ad80ca7bf8c670b65b80cb092c7d2.py │ ├── f4486595a4c229797a00be326b58743a5a386e343d70c54d451de212f97a7a8b.py │ ├── 18d796b7551d2dbabbd6e7e046a3dd2e13d3e758c0cedf5448e096674f930a69.py │ ├── 2680ea2b168d077ec7ad4d22c7897f402c4984d93b314a8566d227ce894a6c14.py │ ├── 2bdd3349496a47f9c3337d85468f31bfc84e49ce1103d6fdefcc7a9e3ba9a97b.py │ ├── 53d2949e479132fe2df4ed3f90130e779adae090933a20ba043cec871f86d4c4.py │ ├── 8a2d183daa29dea1fdad279688a2b99dcfef00932e06d4693385dfc1634c6f6c.py │ ├── 16cd52dc07475330c6fb5599659e496621490fb8b7eb79b82be8b7c78f4349e3.py │ ├── 22bc344a43584c051d8962116e8fd149d72e7e68bcb54caf201ee6e78986b167.py │ ├── 3a50eb1aed494e7b3b7e6fc814943b6b24acafdbc5b40644b79ec50bdb29b023.py │ ├── 9b595859f0a0794adff061138a9a2bb28c49ccda6f6f5665f4063f7f6dc9d8a3.py │ ├── b1ee3667a041c1cb3f7a1ed56cadd96a0ed654d758efe83b4c5a9e1a4e835ef9.py │ ├── baeab95d7182162c404ae9662cf716dba0a7d7c7662266307ae591a273993ea5.py │ ├── 4d3f8fa60aa762d76852454257bd527e40f38549b584e45269cfc5977248fb62.py │ ├── 7f9ef841e54b680b479c91f764049ae8ca04539f9b9484af307c978d9155df4b.py │ ├── cc6fb17536aa3eaba4744e3ade2f5abf4cf21776c7010536bdf284057ff5c794.py │ ├── 318c09f5abc7ace5b69c55380616ebf1cc984e6c365ff8d9f021725eabbe02ae.py │ ├── 0c0432e0f336603f3df2b101f3ec0a296a8eada28765e69615dfa0c04283f0e5.py │ ├── 3e40f2921fbaf6ccbabb2fa5c3cd3a42b54914d66f7a67f8b2ade36f89ed761b.py │ ├── a3e5e6a6e7ed5f85a06f07635b55dca6b66968144709d4f6c74d54a526ab6a92.py │ ├── LOAD_NAME_ast_AugAssign.py │ ├── deadcode_string.py │ ├── e0d5430c6d9ee891b8adb6dc69d571ada7b1da181b905e47bc09bd291b90b3db.py │ ├── e463c913f286b9b53abcb9cca02fa76b9d85c4e100607ed26947a49b42bb0499.py │ ├── c069969a742f2faa3f432cb50c36f58a8158cdae7c19ce0536464b3f3e4b1dd9.py │ ├── 2bfb9b3e488bd50d6e05a300b7cec608d2b0d54260b7ca4ce834bd81a96e1e54.py │ ├── 73d0fdb16164fc57d2dfc4fad6182f18b9c004a668645de8c14219daa546e2d1.py │ ├── 7532e0e7ee9c85347bb4bfcc8751604bf934d3e96e48f3bc8b5778d7856d5a7e.py │ ├── 289bcf3ad1de1e72dab056250fe9516668937d7d9c55be773acd60a8f05c4318.py │ ├── 2ad55a93b62d942e84a4ec6cf3385da73bfe13a70739c8b6e21d6917cdf54142.py │ ├── d75139e3ff11bb53f665648da0259d1189d3a0c2ba16f45fc3e6de8bc7af19e9.py │ ├── 8441cf858d49ebf67356c5b1bf2deebe187639f2ec0c0b3670013cab587fd7a3.py │ ├── 0126981e43aec288449c540bef895abc32c6046ac22095919e1a1564ece7160b.py │ ├── except_cleanup.py │ ├── dcf515466528197be9497b7a599fedb9ad7837e4c66d9356a426ce86c8742123.py │ ├── 0675309754ba4277c9cb3c52d7131377fe69c7744a271e2b7471917dabb5aaa1.py │ ├── c6c630168a85521e9dfac600b5be2f4d24e48c3e61c9c6f6dcb7faf41d9cadb0.py │ ├── d88dbf79f1c03ac9d231408b03584e8396ab41a959edecfde86b82df8ee7c918.py │ ├── 3946430f5c3048d7d85d5424c4fcb541db50c9c41d5653c977e974351b2f6bc9.py │ ├── f4962cd6e6b77f4f1d6f676de32f29200067bf3ac05c63e1d60ef2823b4d1b10.py │ ├── deadcode_listcomp.py │ ├── 83c8b55804e611761085ef55ca3c6aaac382500b797dfa4b797abdbedb23126b.py │ ├── e920a2f2a7dd13d874987bed61fd5e18b8a63c9e838c24f6ad0aa08d3391bf04.py │ ├── 5b248fd091bffcd4c82adafd17760cc0d471f60c584c91a3998b7f213029cee5.py │ ├── 49e298556393765fe40e351447825acca36bbc6b254c144c6a8fc9f82602c6a0.py │ ├── 6a33e6efd8198ed5610a6072c40faa1d1f0baa64ac2b14fb011ab24e5b32f941.py │ ├── 764af8204d7aa6e6c3d1856387308718719eb5e79ca7aa7f68561d74f9748a8d.py │ ├── caf1bb66048f7170dc753f62f472dbee9caa8f4932d49ac60741a1b7998d4405.py │ ├── f533555a1d2d56090b3757364cecf29604df0881e66395fa9533122623793bd9.py │ ├── b8ccb32482909adb7b5677562337973df9562487a306bc7a9d5e43e626fa47ef.py │ ├── 927e4c80a3ef17328f8807a49fe82da7f223652fccfeab7484194b94007c2b74.py │ ├── dbe6280977fa2e696702da4231bab0e84973b6b16a22215bf7d4f10d8b160c1b.py │ ├── 59b33fb13ecb2036cd31a92dac7eded938aab8b9e91f84c80eab0e8bec0441f0.py │ ├── e7b78c6892baf1b134f78b3ba897ee6592b11358c9e0962880526cd44c4e258d.py │ ├── 0e52e1c718dbbe376fa8af89ea22dedd7431a02558e6065951ef712c3ed9e01a.py │ ├── 6105c9bae9aa36ffe1ac55a3c957c54952732ee47573522d1c19779c6c6e6d48.py │ ├── e796977fbd5072af21adf19dd5ea5b6dd5e8affb91d39e280718aea2a7736fb9.py │ ├── 1a7451e2c71947902b4ea93eb2cbf3ca9ecedc07be7d6c6a9832731fca80924a.py │ ├── 477815a6eac78431cfb30ac314764dbf43beed5b54af64c4a666537afaf3d718.py │ ├── 5da4523399bd1a8477d2d02970672787c08a73c1c5f0268b0d08de4c35a7f5a0.py │ ├── 16b59c539eacafcbb66df969aa7a3a6f612fd4fa9acb07a2a30f837350c555a4.py │ ├── 6d0092f79094500dd7bfbe51cedab7e44db1745b729fb347f9a60dff7b96d09a.py │ ├── c919aaad611db2ffd30920265f6e811c28825591b226662ebaadc001c3b6c8e6.py │ ├── 248c72657ee1942a557f5521fe25423a53aa4b44e7890abf76ebaf1960952f2f.py │ ├── 5182c37c0eb2a1b7402d1c3fc623d4e57106429cc932cce07630fc07781e4167.py │ ├── d6d7ea7eb0c3608d8e642a9f78cce7ac05f00f9615d424cc902606412fff6809.py │ ├── 437c7cbd0809c59a17c3928b83eb0a4f2c23aeef11417cdadea9bdefe77c318a.py │ ├── 5ccbe1d4d1e9e02d612eda06a38b753f56c8e20a4780d2912230274c76d2568b.py │ ├── 994c59ca460328030a4a36060ee6143c6699a06c28192a779af62124c93476d9.py │ ├── cc6ef858e370019db11a9d07b4e6439b84ae021422241e69addc8a12b5b1a071.py │ ├── 05b14e50cca9fbffd0ad2b1fa4e2e1243483dc6ea7fe81035c10b41a0d3bd711.py │ ├── 2fa69a57eb3b16f9ce0d5be427ad471c93a54c6d7e68b5be024b6a046ecc6bb2.py │ ├── ff54d68f6c5751c3d7316722b049b34801c6e28b67bb8338490b4fee32c3e4c5.py │ ├── 69bd4dd67f6d3737f72f314978488dfedd5e8071b45a2648757451ac47bf1b33.py │ ├── 9f7d4ed823cb426db2e435dd7856f07c9965a88102bd63e5640a7e63250e2718.py │ ├── load_deref.py │ ├── a6428177defd48080b32b7b570aa729067b9c8044821dbbb787cce7fb3999e73.py │ ├── b80fbb517bb6b016947288beb9d24c05bbd3aa334585fd0f4a13bf25f7a3cca5.py │ ├── ba8bf627448498ff15c46e5e5549e2f2fc2716d45309504753d1dce79842bb0e.py │ ├── e5c099c5f3fe5e4a9cda029696aed3a678be6909cc5e2a78491fd9f54e88200f.py │ ├── c8004683dc7b5fb7ce5fab343696f152cb5a4b39eecfb6bf4a24c0b0847becc1.py │ ├── 37db52dbd6fe707c1f18121b6b0d10ed1b2c7962d3989d3087ab7c1aa207f773.py │ ├── 339587c1bea1dae03e45ad87a1c245a4e7a3d719968e39e572cb987ee055ad50.py │ ├── 635d56ede8cbcb2824d42291eb9fe9288d5f09c768636aaa19984ffdfc91e9fe.py │ ├── 7b46d9fff0efc0a4578c804c7b48af4c6fd9010a93b50865a80075e0320098ef.py │ ├── a3ab7db4c4e846bc2e660b97d021ff2167b71e1609ed29a92c9edd70332adc34.py │ ├── 508ccd0dcac13ecee6f0cea939b73ba5319c780ddbb6c496be96fe5614871d4a.py │ ├── d964710ed091aede2a79d0aafd4177ab682cca90f4909f9d6a2088110b30cbf3.py │ ├── 4ac35edc8adbe59cee1c11080c0e7d458fa5f2144a3f3bc1919e8722d6949780.py │ ├── b38dc0d61808291d3c0a1bb2ed6a6a488a3df729d2307d426f1a101c9e512f06.py │ ├── a913b7799aa6a860097fb4daf0eb8954b97dad48e4880fa3ed76da5e9f736764.py │ ├── f676fbfc99ca7fd38ceb2f4a2ec5edc49fdbc9f47685f14c6fde6239f2bc1590.py │ ├── fe0fdfd09575f3cc013c0bc30a257104d3219a00fd6f777303ca17401f09ad9d.py │ ├── a9cf2f563ac80ad0066c4ef3ac10fa2f146252db49b3a0a9cb69e0c03e07827b.py │ ├── 42a37b8a823eb2e510b967332661afd679c82c60b7177b992a47c16d81117c8a.py │ ├── 9f0ed8b1be98c481ba17f3f3fa0442cc67ae5730f8e854e23e3f3147f9bc5e8b.py │ ├── 82697c5032f0c92b55da4d36c5b7ce2bba016eda66bb1d449e52afe319b447ea.py │ ├── 88f983dcf04b0d0b09cb105943226d186f9f6a8ada3da7c48a1a3a1c0d947995.py │ ├── 16f6f132d75df7cbbd0744f1cf6302dd4a1605a3b6ae0d6e9d45189f19db860b.py │ ├── b0f8cf7dd6323f9df8f3d5f38044c76f0688ea01b1c085b5867eb844780d9c23.py │ ├── 1201463cc1031818ae1ea2b5a9dfcac2b7f9035aaa0c1670e61d8009e5701311.py │ ├── 6e57262c88b56d0d657064aec550809ad12bb5f22524f12b25dfa8840a9856e2.py │ ├── c9b64c28f424df67919f825adf4f93dfa774463768fc26cd36964121d34e28dd.py │ ├── 08b7d79dd83ba10104039e2873d3ea4452bedbb3e46e729114a27536cd9dfdc3.py │ ├── 6a14a6ef8c8540838e00af19ff0b68dde868ec14a774a501cad3f055a40dd23e.py │ ├── 770facda5ac10a6e33d18979e6a420423df9fead73483fda002ff8a241c2e795.py │ ├── 81aed400e9ebc64f9f1f6d0fdaf6ccaefd81736d54d180db3c5a1b2aeb72fb6f.py │ ├── 3dbbed07d7fec1ba458ca74840ec2f6196114bb49f09193a7704911172b8c04a.py │ ├── 93f00cfd350495557ead44e2dd46ac4ce7d913f6e5d86ba1b0d5044cec188438.py │ ├── 9a30338a97c5bd67283cb31910c04624714cbf6c19f1382af18efccd443abff9.py │ ├── d98e27d8963331b58e4e6b84c7580dafde4d9e2980ad4277ce55e6b186113c1d.py │ ├── fc6eb521024986baa84af2634f638e40af090be4aa70ab3c22f3d022e8068228.py │ ├── 227620e1f473c9ac5ccf8328ed28b169c31617f5a4d6302337ce4f83109f745e.py │ ├── d06afcef230a0babce66218961fb7dc171e71ae19f194ebf34e85e3c9501eda7.py │ ├── c1a8b67677b66314b60f56743bfa80e17c35cf4f5c4ab2c890c693969db41fdc.py │ ├── 45a20d9fefe2db8c5fbc91ad26e8e9dce0667e126dab5e34fd6825c085776be5.py │ ├── 780d1e5c82a0d4c02798ef5d5f2f8895334a242128f7a7a300f44cf2036b8dc4.py │ ├── 0fe16ec438396e003734858ad326d907e514273262547abaee0cd15e6eb7083e.py │ ├── 4851dc1b626a95e97dbe0c53f96099d165b755dd1bd552c6ca771f7bca6d30f5.py │ ├── 6602443ba655e0e139cfb923d581f8da91071d631a42f81253c1e47fdc952da3.py │ ├── 4fb0f58b18c1b1484aec340e1a7ab7cdc87185a067ee37a73f05eb1a1d08bb11.py │ ├── 9b3db37076d3c7c76bdfd9badcc70d8047584433e1eea89f45014453d58bbc43.py │ ├── 0194cd769109c110d50905631c5793818736ff3835dd0a5ef97ed2f34cd65892.py │ ├── 7fff707ddf87544616f198155d98e215700b261e39fda3844940ec2aab5a0610.py │ ├── a33f71955b90ec91ef184fccf593e537aa583db12b67d9130917a4349701eaa2.py │ ├── e8a94f0a994ea84627b7cbc9132cf519ccaa6d756f8cb3458cf019e672a510a4.py │ ├── 736316309812e0ca8306cb7eec4c20d75f8e3c45cab3cd46cf4370524ada6823.py │ ├── 91f5b684f56b415a61d211027904e023f2371952602a8d71c17416d8fa3ceed7.py │ ├── ea94f24a1d9b57c7a7d62c428082790caae2fa16429db2e58b2f4addb67a1964.py │ ├── 2f31d64a74ed18026d7000094cefc6e5cace573b0df257f23299ed7bb21a3264.py │ ├── 206e0609ff0589a0a32422ee902f09156af91746e27157c32c9595d12072f92a.py │ ├── 2ab181c0e2d4978b1dc22cb8d9a4ed3e2bc6df02267f9766dfe1e8ee7924ff7c.py │ ├── cb33a25d03eeb972e05ef6585afab710a204477850e8b0f9bad0482f9b4364b2.py │ ├── 6d981a27f4f4672f186cb498c6c012ac2c6168dfd631e7fff5602e9e5586f00e.py │ └── 46597f8f896f11c5d7f432236344cc7e5645c2a39836eb6abdd2437c0422f0f4.py ├── global_tester_calls.py ├── conftest.py ├── test_ipython.py ├── mutmut_workflow.py ├── analyse.py ├── samples │ ├── import_hook.py │ ├── ipython.py │ ├── utils2.py │ ├── utils.py │ ├── configuration.py │ ├── db.py │ ├── server.py │ └── tracer2.py ├── deadcode.py ├── generate_small_sample.py └── utils.py ├── MANIFEST.in ├── setup.py ├── pyproject.toml ├── make_release.sh ├── tox.ini ├── LICENSE.txt ├── setup.cfg ├── development.md ├── .gitignore ├── .github └── workflows │ └── test.yml └── README.md /executing/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | -------------------------------------------------------------------------------- /tests/not_code.txt: -------------------------------------------------------------------------------- 1 | this isn't code 2 | -------------------------------------------------------------------------------- /tests/sample_results/_parser-py-2.7.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/_parser-py-3.5.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/_parser-py-3.6.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/_parser-py-3.7.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/_parser-pypy-2.7.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/_parser-pypy-3.5.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/_parser-pypy-3.6.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-py-2.7.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-py-3.5.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-py-3.6.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-py-3.7.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-pypy-2.7.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-pypy-3.5.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /tests/sample_results/datetime-pypy-3.6.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name="executing") 4 | -------------------------------------------------------------------------------- /tests/small_samples/0800ffcd43ac2c29114cbd591705492110e832d63345d278bbec93a0fc3c64e1.py: -------------------------------------------------------------------------------- 1 | not 0 in 0 -------------------------------------------------------------------------------- /tests/small_samples/7ec5eed65083de4adc88655f15d02ce24303aa1f2860762d800d7e9093c6eb59.py: -------------------------------------------------------------------------------- 1 | (*t_args,) -------------------------------------------------------------------------------- /tests/small_samples/126f1cda2062caf2c74dcdcd77f5cb3a0e2d20b749329f84f48aa4d90701f2d0.py: -------------------------------------------------------------------------------- 1 | session[:100] -------------------------------------------------------------------------------- /tests/small_samples/ae9b3821230822abe9910bb1ebfe74ff2cedc19f646975fb2931f4b67fd4f189.py: -------------------------------------------------------------------------------- 1 | tuple[*a] 2 | -------------------------------------------------------------------------------- /tests/small_samples/1a1292769ffae5636aa864bec0bf01dc60380a867a7f5b8ab6f18b523848bb39.py: -------------------------------------------------------------------------------- 1 | -tester is +tester -------------------------------------------------------------------------------- /tests/small_samples/1afa3b7586b5527d711d8881bc2de395ebb8036a6c91a0cd952f02f280c83a4f.py: -------------------------------------------------------------------------------- 1 | not self == other -------------------------------------------------------------------------------- /tests/small_samples/28dde5e66130457326139e9f4ae62987606de2b6362b073ae3671ad38cf37046.py: -------------------------------------------------------------------------------- 1 | not not something -------------------------------------------------------------------------------- /tests/small_samples/393abb63bcdcf6c3f6b008fb7a7635ac6f615f1ef83579b2ad0e735d738e9ddd.py: -------------------------------------------------------------------------------- 1 | type _Func = Callable -------------------------------------------------------------------------------- /tests/small_samples/867a476c53701b28e25aa4b445e586b8a6764f9f8582b98955c4352b0d8ba415.py: -------------------------------------------------------------------------------- 1 | type TA1[B] = dict -------------------------------------------------------------------------------- /tests/small_samples/ad8aa993e6ee4eb5ee764d55f2e3fd636a99b2ecb8c5aff2b35fbb78a074ea30.py: -------------------------------------------------------------------------------- 1 | (i async for i in arange) -------------------------------------------------------------------------------- /tests/small_samples/dc592fb930b28fe2b7181bec5d6f4b871bdd55c01bc22b0623e610eec70df7ab.py: -------------------------------------------------------------------------------- 1 | from _datetime import * -------------------------------------------------------------------------------- /tests/small_samples/0f7396913445f02c6720c718315895cdb6a259875f6b6fd29259a96c7e9c1ff5.py: -------------------------------------------------------------------------------- 1 | super(mcs, *args, **kwargs) -------------------------------------------------------------------------------- /tests/small_samples/83f4e6cc115c0532d42ca1d428a10c4dd4ee966674cc7bbc7e4d53e784e6f4ff.py: -------------------------------------------------------------------------------- 1 | def f[**P = 0](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/a2389f211aec4c553b1cec683b416480636d7c13d10e8db50b5da567192ce42f.py: -------------------------------------------------------------------------------- 1 | class Foo[__T]: 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/b9fe280619199e07642f4e0d263716394022a3311441d703bf943c3a5115ab40.py: -------------------------------------------------------------------------------- 1 | async def _ag(): 2 | yield -------------------------------------------------------------------------------- /tests/small_samples/e6e45407a25ab0cc42cf0eb707eacccd2d27a0aa5a00bd9bc6df69414f952c42.py: -------------------------------------------------------------------------------- 1 | def f[*Ts = 0](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/e721e8b2c275d578d37aa986c1de68f79a266f8950ce2a74604630d4b0387d85.py: -------------------------------------------------------------------------------- 1 | def f[T = 0](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/09c2b2709b3507baf8a9e749c6e3bed21728d2159858adcfa66c49bb3e2f35f2.py: -------------------------------------------------------------------------------- 1 | def func[T](*, b='b'): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/2b4b06fbfb9588ba2f85687c472bd0a2b74bf299ad3591b3957eb99dfb9342fb.py: -------------------------------------------------------------------------------- 1 | all((something for num in nums)) -------------------------------------------------------------------------------- /tests/small_samples/8519155d8a424c7cbc4bc15042d50c3193688c600ac9552f9503672e7c01b4d9.py: -------------------------------------------------------------------------------- 1 | class A(0, 1, 2, **d): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/8d7d8e2330522993cf517ba2f4191e01c336fb27bbbfa40815629432b96d74fa.py: -------------------------------------------------------------------------------- 1 | class SupportsAbs[T]: 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/b8be3216d583e1ce26486ee2aff5cbe74fed88ebba7118db80a375c9514d1bc1.py: -------------------------------------------------------------------------------- 1 | def func[S, T: S](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/f09192915e250c0e1630b5d9add1328874fcb799cc508db1d7b6a880b2d0acea.py: -------------------------------------------------------------------------------- 1 | assert self is None or frame, () -------------------------------------------------------------------------------- /tests/small_samples/1fff64406be948c8ed049b0fc299b0e3702064f8592d6a020136cc0485e348cf.py: -------------------------------------------------------------------------------- 1 | async def coroutine[B](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/3d740a1da7646802a0c11ca94604e6da910ef47148032a1ec0a50047b9fcc024.py: -------------------------------------------------------------------------------- 1 | def more_generic[*Ts](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/495122dc195a96d0c32cddce6153504521b6bea10cbe5faa1313062c57f198a0.py: -------------------------------------------------------------------------------- 1 | """""" 2 | __dataclass_fields__: ClassVar -------------------------------------------------------------------------------- /tests/small_samples/524a7a805db753f5ea998182ddaea49a177b75a7ae88ab77eaa879755857a15a.py: -------------------------------------------------------------------------------- 1 | class Parent: 2 | type TA1 = dict -------------------------------------------------------------------------------- /tests/small_samples/a35a3b669faf7fa486c7ca0a95a61efab9591a0d1677406c48c53e1a04ebe850.py: -------------------------------------------------------------------------------- 1 | def override[F: _Func](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/a8c5d8fec98be9e324c50d6c89b207e35f3ad80ca7bf8c670b65b80cb092c7d2.py: -------------------------------------------------------------------------------- 1 | def more_generic[**P](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/f4486595a4c229797a00be326b58743a5a386e343d70c54d451de212f97a7a8b.py: -------------------------------------------------------------------------------- 1 | def reveal_type[T]() -> T: 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/18d796b7551d2dbabbd6e7e046a3dd2e13d3e758c0cedf5448e096674f930a69.py: -------------------------------------------------------------------------------- 1 | type unique_name_0[T] = [lambda: T for T in T] -------------------------------------------------------------------------------- /tests/small_samples/2680ea2b168d077ec7ad4d22c7897f402c4984d93b314a8566d227ce894a6c14.py: -------------------------------------------------------------------------------- 1 | def more_generic[**P](): 2 | type TA = P -------------------------------------------------------------------------------- /tests/small_samples/2bdd3349496a47f9c3337d85468f31bfc84e49ce1103d6fdefcc7a9e3ba9a97b.py: -------------------------------------------------------------------------------- 1 | class FunctionProfile: 2 | ncalls: str -------------------------------------------------------------------------------- /tests/small_samples/53d2949e479132fe2df4ed3f90130e779adae090933a20ba043cec871f86d4c4.py: -------------------------------------------------------------------------------- 1 | [0] 2 | unique_name_0: 0 3 | unique_name_1: 0 -------------------------------------------------------------------------------- /tests/small_samples/8a2d183daa29dea1fdad279688a2b99dcfef00932e06d4693385dfc1634c6f6c.py: -------------------------------------------------------------------------------- 1 | def func[T](a='a', *, b='b'): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/16cd52dc07475330c6fb5599659e496621490fb8b7eb79b82be8b7c78f4349e3.py: -------------------------------------------------------------------------------- 1 | def func1[B: 0 | unique_name_0](): 2 | pass -------------------------------------------------------------------------------- /tests/small_samples/22bc344a43584c051d8962116e8fd149d72e7e68bcb54caf201ee6e78986b167.py: -------------------------------------------------------------------------------- 1 | 2 | (session 3 | .filter_by(key=item)) 4 | 5 | -------------------------------------------------------------------------------- /tests/small_samples/3a50eb1aed494e7b3b7e6fc814943b6b24acafdbc5b40644b79ec50bdb29b023.py: -------------------------------------------------------------------------------- 1 | def test_alias_value_01(): 2 | type TA1 = int -------------------------------------------------------------------------------- /tests/small_samples/9b595859f0a0794adff061138a9a2bb28c49ccda6f6f5665f4063f7f6dc9d8a3.py: -------------------------------------------------------------------------------- 1 | global name_type_alias 2 | type name_type_alias = 0 -------------------------------------------------------------------------------- /tests/small_samples/b1ee3667a041c1cb3f7a1ed56cadd96a0ed654d758efe83b4c5a9e1a4e835ef9.py: -------------------------------------------------------------------------------- 1 | def __annotate__(): 2 | (0).__annotate__ 3 | -------------------------------------------------------------------------------- /tests/small_samples/baeab95d7182162c404ae9662cf716dba0a7d7c7662266307ae591a273993ea5.py: -------------------------------------------------------------------------------- 1 | def func[S, T: Sequence[S]](): 2 | pass 3 | -------------------------------------------------------------------------------- /tests/small_samples/4d3f8fa60aa762d76852454257bd527e40f38549b584e45269cfc5977248fb62.py: -------------------------------------------------------------------------------- 1 | type ConstrainedGenericAlias[LongName: (str,)] = list -------------------------------------------------------------------------------- /tests/small_samples/7f9ef841e54b680b479c91f764049ae8ca04539f9b9484af307c978d9155df4b.py: -------------------------------------------------------------------------------- 1 | assert len == 2, 'Watch extra must return pair or None' -------------------------------------------------------------------------------- /tests/small_samples/cc6fb17536aa3eaba4744e3ade2f5abf4cf21776c7010536bdf284057ff5c794.py: -------------------------------------------------------------------------------- 1 | type unique_name_0 = [lambda: T for T in unique_name_1] -------------------------------------------------------------------------------- /tests/small_samples/318c09f5abc7ace5b69c55380616ebf1cc984e6c365ff8d9f021725eabbe02ae.py: -------------------------------------------------------------------------------- 1 | class Outer: 2 | 3 | class Inner[C]: 4 | pass -------------------------------------------------------------------------------- /tests/small_samples/0c0432e0f336603f3df2b101f3ec0a296a8eada28765e69615dfa0c04283f0e5.py: -------------------------------------------------------------------------------- 1 | from test.support import check_syntax_error 2 | unique_name_0: 0 -------------------------------------------------------------------------------- /tests/small_samples/3e40f2921fbaf6ccbabb2fa5c3cd3a42b54914d66f7a67f8b2ade36f89ed761b.py: -------------------------------------------------------------------------------- 1 | async def wait(): 2 | async with something: 3 | pass -------------------------------------------------------------------------------- /tests/small_samples/a3e5e6a6e7ed5f85a06f07635b55dca6b66968144709d4f6c74d54a526ab6a92.py: -------------------------------------------------------------------------------- 1 | class X: 2 | 3 | def f2(__a, /, __b): 4 | (__a, __b) -------------------------------------------------------------------------------- /tests/small_samples/LOAD_NAME_ast_AugAssign.py: -------------------------------------------------------------------------------- 1 | # LOAD_NAME is mapped to ast.Name(ctx=ast.Store) 2 | 3 | class _Hook_amd64: 4 | __float_types += () 5 | -------------------------------------------------------------------------------- /tests/small_samples/deadcode_string.py: -------------------------------------------------------------------------------- 1 | 2 | b'\x00' * 10000 3 | 4 | if not b'\0' *10000: 5 | print(5) 6 | 7 | if not b'\0' *4000: 8 | print(5) 9 | -------------------------------------------------------------------------------- /tests/small_samples/e0d5430c6d9ee891b8adb6dc69d571ada7b1da181b905e47bc09bd291b90b3db.py: -------------------------------------------------------------------------------- 1 | class SupportsAbs[T]: 2 | 3 | def __abs__() -> T: 4 | pass -------------------------------------------------------------------------------- /tests/small_samples/e463c913f286b9b53abcb9cca02fa76b9d85c4e100607ed26947a49b42bb0499.py: -------------------------------------------------------------------------------- 1 | def __call__(func): 2 | 3 | async def inner(): 4 | func -------------------------------------------------------------------------------- /tests/small_samples/c069969a742f2faa3f432cb50c36f58a8158cdae7c19ce0536464b3f3e4b1dd9.py: -------------------------------------------------------------------------------- 1 | def test_alias_value_01(): 2 | type TA1 = int 3 | type TA2 = TA1 -------------------------------------------------------------------------------- /tests/small_samples/2bfb9b3e488bd50d6e05a300b7cec608d2b0d54260b7ca4ce834bd81a96e1e54.py: -------------------------------------------------------------------------------- 1 | def get_local_reprs(): 2 | vars_order = something 3 | lambda: vars_order -------------------------------------------------------------------------------- /tests/small_samples/73d0fdb16164fc57d2dfc4fad6182f18b9c004a668645de8c14219daa546e2d1.py: -------------------------------------------------------------------------------- 1 | class Tester: 2 | 3 | def __setattr__(): 4 | super 5 | __add__ -------------------------------------------------------------------------------- /tests/small_samples/7532e0e7ee9c85347bb4bfcc8751604bf934d3e96e48f3bc8b5778d7856d5a7e.py: -------------------------------------------------------------------------------- 1 | class _IdentityCallable: 2 | 3 | def __call__[T]() -> T: 4 | pass -------------------------------------------------------------------------------- /tests/small_samples/289bcf3ad1de1e72dab056250fe9516668937d7d9c55be773acd60a8f05c4318.py: -------------------------------------------------------------------------------- 1 | class VerifierFailure: 2 | 3 | def __init__(self): 4 | super().__init__ -------------------------------------------------------------------------------- /tests/small_samples/2ad55a93b62d942e84a4ec6cf3385da73bfe13a70739c8b6e21d6917cdf54142.py: -------------------------------------------------------------------------------- 1 | class Any(metaclass=_AnyMeta): 2 | 3 | def __new__(cls): 4 | super().__new__ -------------------------------------------------------------------------------- /tests/small_samples/d75139e3ff11bb53f665648da0259d1189d3a0c2ba16f45fc3e6de8bc7af19e9.py: -------------------------------------------------------------------------------- 1 | class ThemeSection: 2 | unique_name_0: 0 3 | 4 | def __post_init__(): 5 | super -------------------------------------------------------------------------------- /tests/small_samples/8441cf858d49ebf67356c5b1bf2deebe187639f2ec0c0b3670013cab587fd7a3.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | with something as done: 3 | for num in todo: 4 | done[num] -------------------------------------------------------------------------------- /tests/small_samples/0126981e43aec288449c540bef895abc32c6046ac22095919e1a1564ece7160b.py: -------------------------------------------------------------------------------- 1 | class _ProtocolMeta: 2 | ((cls) for attr in _get_protocol_attrs) 3 | super().__instancecheck__ -------------------------------------------------------------------------------- /tests/small_samples/except_cleanup.py: -------------------------------------------------------------------------------- 1 | 2 | try: 3 | pass 4 | except KeyError as err: 5 | del err 6 | 7 | try: 8 | pass 9 | except KeyError as err: 10 | err = 5 11 | -------------------------------------------------------------------------------- /tests/small_samples/dcf515466528197be9497b7a599fedb9ad7837e4c66d9356a426ce86c8742123.py: -------------------------------------------------------------------------------- 1 | class TypeAliasPickleTest: 2 | type ClassLevel = str 3 | 4 | def test_pickling_local(): 5 | pass -------------------------------------------------------------------------------- /tests/small_samples/0675309754ba4277c9cb3c52d7131377fe69c7744a271e2b7471917dabb5aaa1.py: -------------------------------------------------------------------------------- 1 | class name_2: 2 | with name_0: # type: ignoresome text 3 | 4 | class name_2[name_5]: 5 | pass -------------------------------------------------------------------------------- /tests/small_samples/c6c630168a85521e9dfac600b5be2f4d24e48c3e61c9c6f6dcb7faf41d9cadb0.py: -------------------------------------------------------------------------------- 1 | def test_partial_evaluation_cell(): 2 | obj = 0 3 | 4 | class RaisesAttributeError: 5 | unique_name_0: obj -------------------------------------------------------------------------------- /tests/small_samples/d88dbf79f1c03ac9d231408b03584e8396ab41a959edecfde86b82df8ee7c918.py: -------------------------------------------------------------------------------- 1 | def test_getsource_on_class_without_firstlineno(): 2 | __firstlineno__ = 0 3 | 4 | class C: 5 | nonlocal __firstlineno__ 6 | -------------------------------------------------------------------------------- /tests/small_samples/3946430f5c3048d7d85d5424c4fcb541db50c9c41d5653c977e974351b2f6bc9.py: -------------------------------------------------------------------------------- 1 | class _ProtocolMeta: 2 | 3 | def __instancecheck__(cls): 4 | ((cls) for attr in _get_protocol_attrs) 5 | super().__instancecheck__ -------------------------------------------------------------------------------- /tests/small_samples/f4962cd6e6b77f4f1d6f676de32f29200067bf3ac05c63e1d60ef2823b4d1b10.py: -------------------------------------------------------------------------------- 1 | class _ProtocolMeta: 2 | 3 | def __instancecheck__(): 4 | ((cls) for attr in _get_protocol_attrs) 5 | super().__instancecheck__ -------------------------------------------------------------------------------- /tests/small_samples/deadcode_listcomp.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [v+1 for v in [] if False] 5 | {v+1 for v in [] if False} 6 | (v+1 for v in [] if False) 7 | {v+1:v+2 for v in [] if False} 8 | 9 | [v+1 for v in [] if v+5 if False if v+6] 10 | -------------------------------------------------------------------------------- /executing/_pytest_utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | 5 | def is_pytest_compatible() -> bool: 6 | """ returns true if executing can be used for expressions inside assert statements which are rewritten by pytest 7 | """ 8 | if sys.version_info < (3, 11): 9 | return False 10 | 11 | try: 12 | import pytest 13 | except ImportError: 14 | return False 15 | 16 | return pytest.version_tuple >= (8, 3, 4) 17 | -------------------------------------------------------------------------------- /tests/small_samples/83c8b55804e611761085ef55ca3c6aaac382500b797dfa4b797abdbedb23126b.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -96,7 +96,7 @@ 6 | # ">>": ast.RShift, 7 | # "&": ast.BitAnd, 8 | # "^": ast.BitXor, 9 | # - "|": ast.BitOr, 10 | # + "XX|XX": ast.BitOr, 11 | # } 12 | # 13 | # 14 | # 15 | 16 | ast | flags -------------------------------------------------------------------------------- /tests/small_samples/e920a2f2a7dd13d874987bed61fd5e18b8a63c9e838c24f6ad0aa08d3391bf04.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -95,7 +95,7 @@ 6 | # "<<": ast.LShift, 7 | # ">>": ast.RShift, 8 | # "&": ast.BitAnd, 9 | # - "^": ast.BitXor, 10 | # + "XX^XX": ast.BitXor, 11 | # "|": ast.BitOr, 12 | # } 13 | # 14 | # 15 | 16 | other ^ self -------------------------------------------------------------------------------- /tests/small_samples/5b248fd091bffcd4c82adafd17760cc0d471f60c584c91a3998b7f213029cee5.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -89,7 +89,7 @@ 6 | # "@": ast.MatMult, 7 | # "//": ast.FloorDiv, 8 | # "/": ast.Div, 9 | # - "%": ast.Mod, 10 | # + "XX%XX": ast.Mod, 11 | # "+": ast.Add, 12 | # "-": ast.Sub, 13 | # "<<": ast.LShift, 14 | # 15 | 16 | '<%s>' % i -------------------------------------------------------------------------------- /tests/small_samples/49e298556393765fe40e351447825acca36bbc6b254c144c6a8fc9f82602c6a0.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -92,7 +92,7 @@ 6 | # "%": ast.Mod, 7 | # "+": ast.Add, 8 | # "-": ast.Sub, 9 | # - "<<": ast.LShift, 10 | # + "XX<>": ast.RShift, 12 | # "&": ast.BitAnd, 13 | # "^": ast.BitXor, 14 | # 15 | 16 | us1 << 8 -------------------------------------------------------------------------------- /tests/small_samples/6a33e6efd8198ed5610a6072c40faa1d1f0baa64ac2b14fb011ab24e5b32f941.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -93,7 +93,7 @@ 6 | # "+": ast.Add, 7 | # "-": ast.Sub, 8 | # "<<": ast.LShift, 9 | # - ">>": ast.RShift, 10 | # + "XX>>XX": ast.RShift, 11 | # "&": ast.BitAnd, 12 | # "^": ast.BitXor, 13 | # "|": ast.BitOr, 14 | # 15 | 16 | n >> 5 -------------------------------------------------------------------------------- /tests/small_samples/764af8204d7aa6e6c3d1856387308718719eb5e79ca7aa7f68561d74f9748a8d.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -86,7 +86,7 @@ 6 | # op_type_map = { 7 | # "**": ast.Pow, 8 | # "*": ast.Mult, 9 | # - "@": ast.MatMult, 10 | # + "XX@XX": ast.MatMult, 11 | # "//": ast.FloorDiv, 12 | # "/": ast.Div, 13 | # "%": ast.Mod, 14 | # 15 | 16 | noise @ noise -------------------------------------------------------------------------------- /tests/small_samples/caf1bb66048f7170dc753f62f472dbee9caa8f4932d49ac60741a1b7998d4405.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -87,7 +87,7 @@ 6 | # "**": ast.Pow, 7 | # "*": ast.Mult, 8 | # "@": ast.MatMult, 9 | # - "//": ast.FloorDiv, 10 | # + "XX//XX": ast.FloorDiv, 11 | # "/": ast.Div, 12 | # "%": ast.Mod, 13 | # "+": ast.Add, 14 | # 15 | 16 | max_rows // 2 -------------------------------------------------------------------------------- /tests/small_samples/f533555a1d2d56090b3757364cecf29604df0881e66395fa9533122623793bd9.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -94,7 +94,7 @@ 6 | # "-": ast.Sub, 7 | # "<<": ast.LShift, 8 | # ">>": ast.RShift, 9 | # - "&": ast.BitAnd, 10 | # + "XX&XX": ast.BitAnd, 11 | # "^": ast.BitXor, 12 | # "|": ast.BitOr, 13 | # } 14 | # 15 | 16 | future_flags & matching_code -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "setuptools_scm[toml]"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.setuptools_scm] 6 | write_to = "executing/version.py" 7 | write_to_template = "__version__ = '{version}'" 8 | 9 | [tool.mypy] 10 | show_error_codes=true 11 | disallow_untyped_defs=true 12 | disallow_untyped_calls=true 13 | warn_redundant_casts=true 14 | 15 | [[tool.mypy.overrides]] 16 | module = "astroid" 17 | ignore_missing_imports = true 18 | 19 | [tool.pytest.ini_options] 20 | python_functions = "test_" 21 | -------------------------------------------------------------------------------- /tests/small_samples/b8ccb32482909adb7b5677562337973df9562487a306bc7a9d5e43e626fa47ef.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -195,7 +195,7 @@ 6 | # deadcode = True 7 | # 8 | # elif isinstance(node, ast.Assert): 9 | # - cnd = self.static_value(node.test, deadcode) 10 | # + cnd = None 11 | # 12 | # if cnd is False: 13 | # node.deadcode = deadcode 14 | # 15 | 16 | assert end -------------------------------------------------------------------------------- /tests/small_samples/927e4c80a3ef17328f8807a49fe82da7f223652fccfeab7484194b94007c2b74.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -116,7 +116,7 @@ 6 | # node.__static_value = True 7 | # 8 | # except AttributeError as e: 9 | # - if e.name != "_Deadcode__static_value": 10 | # + if e.name != "XX_Deadcode__static_valueXX": 11 | # raise 12 | # 13 | # def static_cnd(self, node): 14 | # 15 | 16 | path if '' else path -------------------------------------------------------------------------------- /tests/small_samples/dbe6280977fa2e696702da4231bab0e84973b6b16a22215bf7d4f10d8b160c1b.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -202,7 +202,7 @@ 6 | # self.walk_deadcode(node.msg, deadcode) 7 | # deadcode = True 8 | # 9 | # - elif cnd is True: 10 | # + elif cnd is not True: 11 | # node.deadcode = deadcode 12 | # self.walk_deadcode(node.msg, True) 13 | # 14 | # 15 | 16 | assert isinstance, (node,) -------------------------------------------------------------------------------- /tests/small_samples/59b33fb13ecb2036cd31a92dac7eded938aab8b9e91f84c80eab0e8bec0441f0.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -204,7 +204,7 @@ 6 | # 7 | # elif cnd is True: 8 | # node.deadcode = deadcode 9 | # - self.walk_deadcode(node.msg, True) 10 | # + self.walk_deadcode(node.msg, False) 11 | # 12 | # else: 13 | # node.deadcode = deadcode 14 | # 15 | 16 | assert '/recipe-release%2F0.6.1', client -------------------------------------------------------------------------------- /tests/small_samples/e7b78c6892baf1b134f78b3ba897ee6592b11358c9e0962880526cd44c4e258d.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -558,7 +558,7 @@ 6 | # node_ctx = getattr(node, "ctx", None) 7 | # 8 | # ctx_match = ( 9 | # - ctx is not type(None) 10 | # + ctx is type(None) 11 | # or not hasattr(node, "ctx") 12 | # or isinstance(node_ctx, ctx) 13 | # ) 14 | # 15 | 16 | self.step_count += 1 -------------------------------------------------------------------------------- /tests/small_samples/0e52e1c718dbbe376fa8af89ea22dedd7431a02558e6065951ef712c3ed9e01a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -67,7 +67,7 @@ 6 | # node.__static_value = node.value 7 | # 8 | # elif isinstance(node, ast.Name) and node.id == "__debug__": 9 | # - node.__static_value = True 10 | # + node.__static_value = None 11 | # 12 | # elif isinstance(node, ast.UnaryOp): 13 | # try: 14 | # 15 | 16 | not __debug__ -------------------------------------------------------------------------------- /tests/small_samples/6105c9bae9aa36ffe1ac55a3c957c54952732ee47573522d1c19779c6c6e6d48.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -95,7 +95,7 @@ 6 | # 7 | # elif isinstance(node, ast.IfExp): 8 | # cnd = self.static_cnd(node.test) 9 | # - if cnd is True: 10 | # + if cnd is False: 11 | # node.__static_value = node.body.__static_value 12 | # 13 | # elif cnd is False: 14 | # 15 | 16 | if 0 if 1 else 2: 17 | print -------------------------------------------------------------------------------- /tests/small_samples/e796977fbd5072af21adf19dd5ea5b6dd5e8affb91d39e280718aea2a7736fb9.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -363,7 +363,7 @@ 6 | # ): 7 | # return 8 | # 9 | # - if inst_match("LOAD_NAME", argval="__annotations__") and node_match( 10 | # + if inst_match("XXLOAD_NAMEXX", argval="__annotations__") and node_match( 11 | # ast.AnnAssign 12 | # ): 13 | # return 14 | # 15 | 16 | data: NestedDict -------------------------------------------------------------------------------- /tests/small_samples/1a7451e2c71947902b4ea93eb2cbf3ca9ecedc07be7d6c6a9832731fca80924a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -192,7 +192,7 @@ 6 | # if isinstance(node, ast.Return): 7 | # self.walk_deadcode(node.value, deadcode) 8 | # 9 | # - deadcode = True 10 | # + deadcode = None 11 | # 12 | # elif isinstance(node, ast.Assert): 13 | # cnd = self.static_value(node.test, deadcode) 14 | # 15 | 16 | raise NotImplementedError 17 | datafiles -------------------------------------------------------------------------------- /tests/small_samples/477815a6eac78431cfb30ac314764dbf43beed5b54af64c4a666537afaf3d718.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -67,7 +67,7 @@ 6 | # node.__static_value = node.value 7 | # 8 | # elif isinstance(node, ast.Name) and node.id == "__debug__": 9 | # - node.__static_value = True 10 | # + node.__static_value = False 11 | # 12 | # elif isinstance(node, ast.UnaryOp): 13 | # try: 14 | # 15 | 16 | if __debug__: 17 | self -------------------------------------------------------------------------------- /tests/small_samples/5da4523399bd1a8477d2d02970672787c08a73c1c5f0268b0d08de4c35a7f5a0.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -200,7 +200,7 @@ 6 | # if cnd is False: 7 | # node.deadcode = deadcode 8 | # self.walk_deadcode(node.msg, deadcode) 9 | # - deadcode = True 10 | # + deadcode = None 11 | # 12 | # elif cnd is True: 13 | # node.deadcode = deadcode 14 | # 15 | 16 | assert False, 'Can this happen?' 17 | nodes -------------------------------------------------------------------------------- /tests/small_samples/16b59c539eacafcbb66df969aa7a3a6f612fd4fa9acb07a2a30f837350c555a4.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -505,7 +505,7 @@ 6 | # elif op_name in ( 7 | # "LOAD_NAME", 8 | # "LOAD_GLOBAL", 9 | # - "LOAD_FAST", 10 | # + "XXLOAD_FASTXX", 11 | # "LOAD_DEREF", 12 | # "LOAD_CLASSDEREF", 13 | # ): 14 | # 15 | 16 | def trace_this_module(self, context=0, deep=False): 17 | context -= 1 -------------------------------------------------------------------------------- /tests/small_samples/6d0092f79094500dd7bfbe51cedab7e44db1745b729fb347f9a60dff7b96d09a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -503,7 +503,7 @@ 6 | # ctx = ast.Load 7 | # extra_filter = lambda e: mangled_name(e) == instruction.argval 8 | # elif op_name in ( 9 | # - "LOAD_NAME", 10 | # + "XXLOAD_NAMEXX", 11 | # "LOAD_GLOBAL", 12 | # "LOAD_FAST", 13 | # "LOAD_DEREF", 14 | # 15 | 16 | basic_types += () -------------------------------------------------------------------------------- /tests/small_samples/c919aaad611db2ffd30920265f6e811c28825591b226662ebaadc001c3b6c8e6.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -353,7 +353,7 @@ 6 | # # call to the generator function 7 | # return 8 | # 9 | # - if inst_match(("CALL", "CALL_FUNCTION_EX")) and node_match( 10 | # + if inst_match(("CALL", "XXCALL_FUNCTION_EXXX")) and node_match( 11 | # (ast.ClassDef, ast.Call) 12 | # ): 13 | # return 14 | # 15 | 16 | create_engine(**kwargs) -------------------------------------------------------------------------------- /tests/small_samples/248c72657ee1942a557f5521fe25423a53aa4b44e7890abf76ebaf1960952f2f.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -279,7 +279,7 @@ 6 | # # else: 7 | # # return None 8 | # # deadcode() 9 | # - deadcode = True 10 | # + deadcode = False 11 | # 12 | # elif isinstance(node, ast.IfExp): 13 | # 14 | # 15 | 16 | def replace(self, string): 17 | for start, end, match in self: 18 | pass 19 | else: 20 | return False 21 | self -------------------------------------------------------------------------------- /tests/small_samples/5182c37c0eb2a1b7402d1c3fc623d4e57106429cc932cce07630fc07781e4167.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -246,7 +246,7 @@ 6 | # 7 | # self.walk_deadcode(node.test, deadcode) 8 | # 9 | # - deadcode = if_is_dead and else_is_dead 10 | # + deadcode = if_is_dead or else_is_dead 11 | # 12 | # elif isinstance(node, ast.Match): 13 | # self.walk_deadcode(node.subject, deadcode) 14 | # 15 | 16 | def before_stmt(self, node, frame): 17 | if frame: 18 | return 19 | isinstance -------------------------------------------------------------------------------- /tests/small_samples/d6d7ea7eb0c3608d8e642a9f78cce7ac05f00f9615d424cc902606412fff6809.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -113,7 +113,7 @@ 6 | # node.__static_value = False 7 | # 8 | # if any(self.static_cnd(n) is True for n in node.values): 9 | # - node.__static_value = True 10 | # + node.__static_value = None 11 | # 12 | # except AttributeError as e: 13 | # if e.name != "_Deadcode__static_value": 14 | # 15 | 16 | if True or type: 17 | obj['Filter'] = [] -------------------------------------------------------------------------------- /tests/small_samples/437c7cbd0809c59a17c3928b83eb0a4f2c23aeef11417cdadea9bdefe77c318a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -473,7 +473,7 @@ 6 | # return 7 | # 8 | # if node_match(ast.Name, ctx=ast.Del) and inst_match( 9 | # - ("DELETE_NAME", "DELETE_GLOBAL"), argval=mangled_name(node) 10 | # + ("XXDELETE_NAMEXX", "DELETE_GLOBAL"), argval=mangled_name(node) 11 | # ): 12 | # return 13 | # 14 | # 15 | 16 | class SerializedDAG: 17 | del __get_constructor_defaults -------------------------------------------------------------------------------- /tests/small_samples/5ccbe1d4d1e9e02d612eda06a38b753f56c8e20a4780d2912230274c76d2568b.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -239,7 +239,7 @@ 6 | # 7 | # test_value = self.static_value(node.test, deadcode) 8 | # 9 | # - if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) 10 | # + if_is_dead = None 11 | # else_is_dead = self.check_stmts( 12 | # node.orelse, deadcode or (test_value is True) 13 | # ) 14 | # 15 | 16 | if isinstance: 17 | is_interesting_expression -------------------------------------------------------------------------------- /tests/small_samples/994c59ca460328030a4a36060ee6143c6699a06c28192a779af62124c93476d9.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -66,7 +66,7 @@ 6 | # if isinstance(node, ast.Constant): 7 | # node.__static_value = node.value 8 | # 9 | # - elif isinstance(node, ast.Name) and node.id == "__debug__": 10 | # + elif isinstance(node, ast.Name) and node.id != "__debug__": 11 | # node.__static_value = True 12 | # 13 | # elif isinstance(node, ast.UnaryOp): 14 | # 15 | 16 | num_samples or dict -------------------------------------------------------------------------------- /tests/small_samples/cc6ef858e370019db11a9d07b4e6439b84ae021422241e69addc8a12b5b1a071.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -535,7 +535,7 @@ 6 | # ctx = ast.Load 7 | # extra_filter = lambda e: mangled_name(e) == instruction.argval 8 | # elif op_name in ( 9 | # - "LOAD_NAME", 10 | # + "XXLOAD_NAMEXX", 11 | # "LOAD_GLOBAL", 12 | # "LOAD_FAST", 13 | # "LOAD_DEREF", 14 | # 15 | 16 | @__has_required_azure 17 | class AzureTest: 18 | pass -------------------------------------------------------------------------------- /executing/_exceptions.py: -------------------------------------------------------------------------------- 1 | 2 | class KnownIssue(Exception): 3 | """ 4 | Raised in case of an known problem. Mostly because of cpython bugs. 5 | Executing.node gets set to None in this case. 6 | """ 7 | 8 | pass 9 | 10 | 11 | class VerifierFailure(Exception): 12 | """ 13 | Thrown for an unexpected mapping from instruction to ast node 14 | Executing.node gets set to None in this case. 15 | """ 16 | 17 | def __init__(self, title, node, instruction): 18 | # type: (object, object, object) -> None 19 | self.node = node 20 | self.instruction = instruction 21 | 22 | super().__init__(title) # type: ignore[call-arg] 23 | -------------------------------------------------------------------------------- /tests/small_samples/05b14e50cca9fbffd0ad2b1fa4e2e1243483dc6ea7fe81035c10b41a0d3bd711.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -241,7 +241,7 @@ 6 | # 7 | # if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) 8 | # else_is_dead = self.check_stmts( 9 | # - node.orelse, deadcode or (test_value is True) 10 | # + node.orelse, deadcode or (test_value is False) 11 | # ) 12 | # 13 | # self.walk_deadcode(node.test, deadcode) 14 | # 15 | 16 | if False: 17 | pass 18 | self -------------------------------------------------------------------------------- /tests/small_samples/2fa69a57eb3b16f9ce0d5be427ad471c93a54c6d7e68b5be024b6a046ecc6bb2.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -197,7 +197,7 @@ 6 | # elif isinstance(node, ast.Assert): 7 | # cnd = self.static_value(node.test, deadcode) 8 | # 9 | # - if cnd is False: 10 | # + if cnd is not False: 11 | # node.deadcode = deadcode 12 | # self.walk_deadcode(node.msg, deadcode) 13 | # deadcode = True 14 | # 15 | 16 | with self as session: 17 | assert isinstance 18 | db_func -------------------------------------------------------------------------------- /tests/small_samples/ff54d68f6c5751c3d7316722b049b34801c6e28b67bb8338490b4fee32c3e4c5.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -464,7 +464,7 @@ 6 | # # and/or short circuit 7 | # return 8 | # 9 | # - if inst_match("DELETE_SUBSCR") and node_match(ast.Subscript, ctx=ast.Del): 10 | # + if inst_match("XXDELETE_SUBSCRXX") and node_match(ast.Subscript, ctx=ast.Del): 11 | # return 12 | # 13 | # if node_match(ast.Name, ctx=ast.Load) and inst_match( 14 | # 15 | 16 | del tracer[frame] -------------------------------------------------------------------------------- /tests/global_tester_calls.py: -------------------------------------------------------------------------------- 1 | from .utils import tester 2 | 3 | # tester calls tested with test_tester at global scope 4 | 5 | assert tester([1, 2, 3]) == [1, 2, 3] 6 | 7 | assert tester.asd is tester 8 | assert tester[1 + 2] is tester 9 | assert tester**4 is tester 10 | assert tester * 3 is tester 11 | assert tester - 2 is tester 12 | assert tester + 1 is tester 13 | assert -tester is +tester is ~tester is tester 14 | assert (tester < 7) is tester 15 | assert (tester >= 78) is tester 16 | assert (tester != 79) is tester 17 | # assert (5 != tester != 6) is tester 18 | assert tester.foo(45, False) == 45 19 | 20 | assert (tester or 234) == 234 21 | assert (tester and 1123) is tester 22 | -------------------------------------------------------------------------------- /tests/small_samples/69bd4dd67f6d3737f72f314978488dfedd5e8071b45a2648757451ac47bf1b33.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -6,7 +6,7 @@ 6 | # "search all child nodes except other loops for a break statement" 7 | # 8 | # if isinstance(node_or_list, ast.AST): 9 | # - childs = ast.iter_child_nodes(node_or_list) 10 | # + childs = None 11 | # elif isinstance(node_or_list, list): 12 | # childs = node_or_list 13 | # else: 14 | # 15 | 16 | with self: 17 | for _ in tester: 18 | pass 19 | else: 20 | raise ValueError -------------------------------------------------------------------------------- /tests/small_samples/9f7d4ed823cb426db2e435dd7856f07c9965a88102bd63e5640a7e63250e2718.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -87,9 +87,7 @@ 6 | # 7 | # elif isinstance(node, ast.Subscript): 8 | # try: 9 | # - node.__static_value = node.value.__static_value[ 10 | # - node.slice.__static_value 11 | # - ] 12 | # + node.__static_value = None 13 | # except Exception: 14 | # pass 15 | # 16 | # 17 | 18 | if hangs[depth]: 19 | hang -------------------------------------------------------------------------------- /tests/small_samples/load_deref.py: -------------------------------------------------------------------------------- 1 | # VerifierFailure: 2 | # ast.Name is not created from LOAD_DEREF 3 | 4 | # instruction: Instruction(opname='LOAD_DEREF', opcode=137, arg=2, argval='_TVMScriptParser__convert_index', argrepr='_TVMScriptParser__convert_index', offset=12, starts_line=None, is_jump_target=False, positions=Positions(lineno=22, end_lineno=22, col_offset=9, end_col_offset=24)) 5 | 6 | # node: Name(id='__convert_index', ctx=Load(), lineno=22, col_offset=9, end_lineno=22, end_col_offset=24) 7 | 8 | class TVMScriptParser: 9 | 10 | def transform_SubscriptAssign(): 11 | 12 | def __convert_index(): 13 | pass 14 | [__convert_index for x in indexes] 15 | -------------------------------------------------------------------------------- /tests/small_samples/a6428177defd48080b32b7b570aa729067b9c8044821dbbb787cce7fb3999e73.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -307,7 +307,7 @@ 6 | # self.check_stmts(node.body, deadcode or cnd is False) 7 | # else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 8 | # 9 | # - if cnd is True and not contains_break(node): 10 | # + if cnd is False and not contains_break(node): 11 | # # while True: ... no break 12 | # deadcode = True 13 | # 14 | # 15 | 16 | while 0: 17 | pass 18 | x = 0 -------------------------------------------------------------------------------- /tests/small_samples/b80fbb517bb6b016947288beb9d24c05bbd3aa334585fd0f4a13bf25f7a3cca5.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -494,7 +494,7 @@ 6 | # op_type = dict( 7 | # UNARY_POSITIVE=ast.UAdd, 8 | # UNARY_NEGATIVE=ast.USub, 9 | # - UNARY_NOT=ast.Not, 10 | # + UNARY_NOTXX=ast.Not, 11 | # UNARY_INVERT=ast.Invert, 12 | # )[op_name] 13 | # extra_filter = lambda e: isinstance(cast(ast.UnaryOp, e).op, op_type) 14 | # 15 | 16 | not self -------------------------------------------------------------------------------- /tests/small_samples/ba8bf627448498ff15c46e5e5549e2f2fc2716d45309504753d1dce79842bb0e.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -341,7 +341,7 @@ 6 | # dead_op = True 7 | # 8 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): 9 | # - dead_op = deadcode 10 | # + dead_op = None 11 | # for v in node.values: 12 | # if self.static_value(v, dead_op) is True: 13 | # dead_op = True 14 | # 15 | 16 | def advancePastEmptyBuckets(): 17 | return 18 | (is_equal or is_equal) -------------------------------------------------------------------------------- /tests/small_samples/e5c099c5f3fe5e4a9cda029696aed3a678be6909cc5e2a78491fd9f54e88200f.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -336,7 +336,7 @@ 6 | # return isinstance(node, node_type) and all( 7 | # isinstance(getattr(node, k), v) 8 | # if isinstance(v, type) 9 | # - else getattr(node, k) == v 10 | # + else getattr(node, k) != v 11 | # for k, v in kwargs.items() 12 | # ) 13 | # 14 | # 15 | 16 | def _trim_arity(func, max_limit=3): 17 | del tb -------------------------------------------------------------------------------- /tests/small_samples/c8004683dc7b5fb7ce5fab343696f152cb5a4b39eecfb6bf4a24c0b0847becc1.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -241,7 +241,7 @@ 6 | # 7 | # if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) 8 | # else_is_dead = self.check_stmts( 9 | # - node.orelse, deadcode or (test_value is True) 10 | # + node.orelse, deadcode and (test_value is True) 11 | # ) 12 | # 13 | # self.walk_deadcode(node.test, deadcode) 14 | # 15 | 16 | if True: 17 | pass 18 | else: 19 | startPos -------------------------------------------------------------------------------- /tests/small_samples/37db52dbd6fe707c1f18121b6b0d10ed1b2c7962d3989d3087ab7c1aa207f773.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -71,9 +71,7 @@ 6 | # 7 | # elif isinstance(node, ast.UnaryOp): 8 | # try: 9 | # - node.__static_value = self.operator_map[type(node.op)]( 10 | # - node.operand.__static_value 11 | # - ) 12 | # + node.__static_value = None 13 | # except Exception: 14 | # pass 15 | # 16 | # 17 | 18 | if not end_lineno: 19 | start_lineno -------------------------------------------------------------------------------- /tests/small_samples/339587c1bea1dae03e45ad87a1c245a4e7a3d719968e39e572cb987ee055ad50.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -337,7 +337,7 @@ 6 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): 7 | # dead_op = deadcode 8 | # for v in node.values: 9 | # - if self.static_value(v, dead_op) is False: 10 | # + if self.static_value(v, dead_op) is True: 11 | # dead_op = True 12 | # 13 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): 14 | # 15 | 16 | print(0 and foo) -------------------------------------------------------------------------------- /tests/small_samples/635d56ede8cbcb2824d42291eb9fe9288d5f09c768636aaa19984ffdfc91e9fe.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -271,7 +271,7 @@ 6 | # self.walk_deadcode(node.iter, deadcode) 7 | # self.check_stmts(node.body, deadcode) 8 | # 9 | # - else_is_dead = self.check_stmts(node.orelse, deadcode) 10 | # + else_is_dead = None 11 | # 12 | # if else_is_dead and not contains_break(node.body): 13 | # # for a in l: 14 | # 15 | 16 | with self: 17 | for _ in tester: 18 | pass 19 | else: 20 | tester -------------------------------------------------------------------------------- /tests/small_samples/7b46d9fff0efc0a4578c804c7b48af4c6fd9010a93b50865a80075e0320098ef.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -239,7 +239,7 @@ 6 | # 7 | # test_value = self.static_value(node.test, deadcode) 8 | # 9 | # - if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) 10 | # + if_is_dead = self.check_stmts(node.body, deadcode or (test_value is True)) 11 | # else_is_dead = self.check_stmts( 12 | # node.orelse, deadcode or (test_value is True) 13 | # ) 14 | # 15 | 16 | if False: 17 | self -------------------------------------------------------------------------------- /tests/small_samples/a3ab7db4c4e846bc2e660b97d021ff2167b71e1609ed29a92c9edd70332adc34.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -391,7 +391,7 @@ 6 | # ): 7 | # return 8 | # 9 | # - if inst_match("BUILD_STRING") and ( 10 | # + if inst_match("XXBUILD_STRINGXX") and ( 11 | # node_match(ast.JoinedStr) or node_match(ast.BinOp, op=ast.Mod) 12 | # ): 13 | # return 14 | # 15 | 16 | ('expression %r caused the wrong ValueError\n' + 'actual error was:\n%s\n' + 'expected error was:\n%s\n') % (expr, e, error) -------------------------------------------------------------------------------- /tests/small_samples/508ccd0dcac13ecee6f0cea939b73ba5319c780ddbb6c496be96fe5614871d4a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -468,7 +468,7 @@ 6 | # return 7 | # 8 | # if node_match(ast.Name, ctx=ast.Load) and inst_match( 9 | # - ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL"), argval=mangled_name(node) 10 | # + ("LOAD_NAME", "LOAD_FAST", "XXLOAD_GLOBALXX"), argval=mangled_name(node) 11 | # ): 12 | # return 13 | # 14 | # 15 | 16 | class __test: 17 | 18 | def onClickPage(self, event): 19 | __test2 -------------------------------------------------------------------------------- /tests/small_samples/d964710ed091aede2a79d0aafd4177ab682cca90f4909f9d6a2088110b30cbf3.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -304,7 +304,7 @@ 6 | # elif isinstance(node, (ast.While)): 7 | # cnd = self.static_value(node.test, deadcode) 8 | # 9 | # - self.check_stmts(node.body, deadcode or cnd is False) 10 | # + self.check_stmts(node.body, deadcode or cnd is True) 11 | # else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 12 | # 13 | # if cnd is True and not contains_break(node): 14 | # 15 | 16 | while True: 17 | node -------------------------------------------------------------------------------- /tests/small_samples/4ac35edc8adbe59cee1c11080c0e7d458fa5f2144a3f3bc1919e8722d6949780.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -99,7 +99,7 @@ 6 | # node.__static_value = node.body.__static_value 7 | # 8 | # elif cnd is False: 9 | # - node.__static_value = node.orelse.__static_value 10 | # + node.__static_value = None 11 | # 12 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): 13 | # if all(self.static_cnd(n) is True for n in node.values): 14 | # 15 | 16 | if 1 if 0 else a: 17 | print -------------------------------------------------------------------------------- /tests/small_samples/b38dc0d61808291d3c0a1bb2ed6a6a488a3df729d2307d426f1a101c9e512f06.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -106,7 +106,7 @@ 6 | # node.__static_value = True 7 | # 8 | # if any(self.static_cnd(n) is False for n in node.values): 9 | # - node.__static_value = False 10 | # + node.__static_value = True 11 | # 12 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): 13 | # if all(self.static_cnd(n) is False for n in node.values): 14 | # 15 | 16 | if outer and False: 17 | self -------------------------------------------------------------------------------- /tests/small_samples/a913b7799aa6a860097fb4daf0eb8954b97dad48e4880fa3ed76da5e9f736764.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -262,7 +262,7 @@ 6 | # @staticmethod 7 | # def is_except_cleanup(inst: dis.Instruction, node: EnhancedAST) -> bool: 8 | # if inst.opname not in ( 9 | # - "STORE_NAME", 10 | # + "XXSTORE_NAMEXX", 11 | # "STORE_FAST", 12 | # "STORE_DEREF", 13 | # "STORE_GLOBAL", 14 | # 15 | 16 | try: 17 | from aiohttp import web 18 | import aiohttp_cors 19 | except ImportError as ie: 20 | pass -------------------------------------------------------------------------------- /tests/small_samples/f676fbfc99ca7fd38ceb2f4a2ec5edc49fdbc9f47685f14c6fde6239f2bc1590.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -79,9 +79,7 @@ 6 | # 7 | # elif isinstance(node, ast.BinOp): 8 | # try: 9 | # - node.__static_value = self.operator_map[type(node.op)]( 10 | # - node.left.__static_value, node.right.__static_value 11 | # - ) 12 | # + node.__static_value = None 13 | # except Exception: 14 | # pass 15 | # 16 | # 17 | 18 | if options & Diagnostic: 19 | ValueError -------------------------------------------------------------------------------- /tests/small_samples/fe0fdfd09575f3cc013c0bc30a257104d3219a00fd6f777303ca17401f09ad9d.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -304,7 +304,7 @@ 6 | # elif isinstance(node, (ast.While)): 7 | # cnd = self.static_value(node.test, deadcode) 8 | # 9 | # - self.check_stmts(node.body, deadcode or cnd is False) 10 | # + self.check_stmts(node.body, deadcode or cnd is not False) 11 | # else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 12 | # 13 | # if cnd is True and not contains_break(node): 14 | # 15 | 16 | while context: 17 | frame -------------------------------------------------------------------------------- /tests/small_samples/a9cf2f563ac80ad0066c4ef3ac10fa2f146252db49b3a0a9cb69e0c03e07827b.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -43,7 +43,7 @@ 6 | # elif isinstance(node, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)): 7 | # name = node.name 8 | # elif isinstance(node, ast.ExceptHandler): 9 | # - name = node.name or "exc" 10 | # + name = node.name or "XXexcXX" 11 | # else: 12 | # raise TypeError("XXno node to mangleXX") 13 | # 14 | # 15 | 16 | def call_errorfunc(): 17 | global _errok, _token, _restart 18 | del _errok, _token, _restart -------------------------------------------------------------------------------- /tests/small_samples/42a37b8a823eb2e510b967332661afd679c82c60b7177b992a47c16d81117c8a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -468,7 +468,7 @@ 6 | # return 7 | # 8 | # if node_match(ast.Name, ctx=ast.Load) and inst_match( 9 | # - ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL"), argval=mangled_name(node) 10 | # + ("XXLOAD_NAMEXX", "LOAD_FAST", "LOAD_GLOBAL"), argval=mangled_name(node) 11 | # ): 12 | # return 13 | # 14 | # 15 | 16 | class Opt: 17 | 18 | def __init__(self, expr: Union, default: Any=__optionalNotMatched): 19 | pass -------------------------------------------------------------------------------- /tests/small_samples/9f0ed8b1be98c481ba17f3f3fa0442cc67ae5730f8e854e23e3f3147f9bc5e8b.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -240,9 +240,7 @@ 6 | # test_value = self.static_value(node.test, deadcode) 7 | # 8 | # if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) 9 | # - else_is_dead = self.check_stmts( 10 | # - node.orelse, deadcode or (test_value is True) 11 | # - ) 12 | # + else_is_dead = None 13 | # 14 | # self.walk_deadcode(node.test, deadcode) 15 | # 16 | # 17 | 18 | if i: 19 | pass 20 | else: 21 | loop -------------------------------------------------------------------------------- /tests/small_samples/82697c5032f0c92b55da4d36c5b7ce2bba016eda66bb1d449e52afe319b447ea.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -305,7 +305,7 @@ 6 | # cnd = self.static_value(node.test, deadcode) 7 | # 8 | # self.check_stmts(node.body, deadcode or cnd is False) 9 | # - else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 10 | # + else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is not True) 11 | # 12 | # if cnd is True and not contains_break(node): 13 | # # while True: ... no break 14 | # 15 | 16 | while context: 17 | pass 18 | os -------------------------------------------------------------------------------- /make_release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eux 3 | 4 | # Ensure that there are no uncommitted changes 5 | # which would mess up using the git tag as a version 6 | [ -z "$(git status --porcelain)" ] 7 | 8 | if [ -z "${1+x}" ] 9 | then 10 | set +x 11 | echo Provide a version argument 12 | echo "${0} .." 13 | exit 1 14 | else 15 | if [[ ${1} =~ ^([0-9]+)(\.[0-9]+)?(\.[0-9]+)?$ ]]; then 16 | : 17 | else 18 | echo "Not a valid release tag." 19 | exit 1 20 | fi 21 | fi 22 | 23 | tox -p 3 24 | 25 | export TAG="v${1}" 26 | git tag "${TAG}" 27 | git push origin master "${TAG}" 28 | rm -rf ./build ./dist 29 | python -m build --sdist --wheel . 30 | twine upload ./dist/*.whl dist/*.tar.gz 31 | -------------------------------------------------------------------------------- /tests/small_samples/88f983dcf04b0d0b09cb105943226d186f9f6a8ada3da7c48a1a3a1c0d947995.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -11,7 +11,7 @@ 6 | 7 | 8 | # def parents(node: EnhancedAST) -> Iterator[EnhancedAST]: 9 | # - while True: 10 | # + while False: 11 | # if hasattr(node, "parent"): 12 | # node = node.parent 13 | # yield node 14 | 15 | 16 | def __call__(self, function): 17 | 18 | def generator_wrapper(*args, **kwargs): 19 | try: 20 | method, incoming = (gen.send, (yield outgoing)) 21 | except Exception as e: 22 | pass -------------------------------------------------------------------------------- /tests/small_samples/16f6f132d75df7cbbd0744f1cf6302dd4a1605a3b6ae0d6e9d45189f19db860b.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -225,7 +225,7 @@ 6 | # node = self.result = cast(EnhancedAST, comparisons[0]) 7 | # else: 8 | # raise KnownIssue( 9 | # - "multiple chain comparison inside %s can not be fixed" % (node) 10 | # + "multiple chain comparison inside %s can not be fixed" / (node) 11 | # ) 12 | # 13 | # else: 14 | # 15 | 16 | if 0 <= r <= 255 and 0 <= g <= 255: 17 | pass -------------------------------------------------------------------------------- /tests/small_samples/b0f8cf7dd6323f9df8f3d5f38044c76f0688ea01b1c085b5867eb844780d9c23.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -105,7 +105,7 @@ 6 | # if all(self.static_cnd(n) is True for n in node.values): 7 | # node.__static_value = True 8 | # 9 | # - if any(self.static_cnd(n) is False for n in node.values): 10 | # + if any(self.static_cnd(n) is not False for n in node.values): 11 | # node.__static_value = False 12 | # 13 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): 14 | # 15 | 16 | if isinstance and node: 17 | self -------------------------------------------------------------------------------- /tests/small_samples/1201463cc1031818ae1ea2b5a9dfcac2b7f9035aaa0c1670e61d8009e5701311.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -448,7 +448,7 @@ 6 | # return 7 | # 8 | # if ( 9 | # - inst_match(("STORE_FAST", "STORE_DEREF", "STORE_NAME", "STORE_GLOBAL")) 10 | # + inst_match(("STORE_FAST", "STORE_DEREF", "STORE_NAME", "XXSTORE_GLOBALXX")) 11 | # and ( 12 | # node_match((ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)) 13 | # or node_match( 14 | # 15 | 16 | keyring = None 17 | 18 | def get_keyring_auth(): 19 | global keyring -------------------------------------------------------------------------------- /tests/small_samples/6e57262c88b56d0d657064aec550809ad12bb5f22524f12b25dfa8840a9856e2.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -264,7 +264,7 @@ 6 | # if inst.opname not in ( 7 | # "STORE_NAME", 8 | # "STORE_FAST", 9 | # - "STORE_DEREF", 10 | # + "XXSTORE_DEREFXX", 11 | # "STORE_GLOBAL", 12 | # "DELETE_NAME", 13 | # "DELETE_FAST", 14 | # 15 | 16 | def filterChanged(self, text): 17 | try: 18 | e = compile(text, '', 'eval') 19 | except Exception as e: 20 | pass 21 | 22 | def f(t): 23 | e -------------------------------------------------------------------------------- /tests/small_samples/c9b64c28f424df67919f825adf4f93dfa774463768fc26cd36964121d34e28dd.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -246,7 +246,7 @@ 6 | # 7 | # self.walk_deadcode(node.test, deadcode) 8 | # 9 | # - deadcode = if_is_dead and else_is_dead 10 | # + deadcode = None 11 | # 12 | # elif isinstance(node, ast.Match): 13 | # self.walk_deadcode(node.subject, deadcode) 14 | # 15 | 16 | def main(argv: Sequence=None) -> int: 17 | with error_handler: 18 | if args: 19 | return uninstall 20 | else: 21 | raise NotImplementedError 22 | AssertionError -------------------------------------------------------------------------------- /tests/small_samples/08b7d79dd83ba10104039e2873d3ea4452bedbb3e46e729114a27536cd9dfdc3.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -305,7 +305,7 @@ 6 | # cnd = self.static_value(node.test, deadcode) 7 | # 8 | # self.check_stmts(node.body, deadcode or cnd is False) 9 | # - else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 10 | # + else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is False) 11 | # 12 | # if cnd is True and not contains_break(node): 13 | # # while True: ... no break 14 | # 15 | 16 | while True: 17 | pass 18 | else: 19 | SSHException -------------------------------------------------------------------------------- /tests/small_samples/6a14a6ef8c8540838e00af19ff0b68dde868ec14a774a501cad3f055a40dd23e.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -239,7 +239,7 @@ 6 | # 7 | # test_value = self.static_value(node.test, deadcode) 8 | # 9 | # - if_is_dead = self.check_stmts(node.body, deadcode or (test_value is False)) 10 | # + if_is_dead = self.check_stmts(node.body, deadcode and (test_value is False)) 11 | # else_is_dead = self.check_stmts( 12 | # node.orelse, deadcode or (test_value is True) 13 | # ) 14 | # 15 | 16 | def _get_connection(self): 17 | return beanstalkc 18 | if self: 19 | beanstalkc -------------------------------------------------------------------------------- /tests/small_samples/770facda5ac10a6e33d18979e6a420423df9fead73483fda002ff8a241c2e795.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -304,7 +304,7 @@ 6 | # elif isinstance(node, (ast.While)): 7 | # cnd = self.static_value(node.test, deadcode) 8 | # 9 | # - self.check_stmts(node.body, deadcode or cnd is False) 10 | # + self.check_stmts(node.body, deadcode and cnd is False) 11 | # else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 12 | # 13 | # if cnd is True and not contains_break(node): 14 | # 15 | 16 | def advancePastEmptyBuckets(): 17 | return 18 | while self: 19 | self -------------------------------------------------------------------------------- /tests/small_samples/81aed400e9ebc64f9f1f6d0fdaf6ccaefd81736d54d180db3c5a1b2aeb72fb6f.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -329,7 +329,7 @@ 6 | # handlers_dead = all( 7 | # [self.check_stmts(h.body, deadcode) for h in node.handlers] 8 | # ) 9 | # - else_dead = self.check_stmts(node.orelse, try_dead) 10 | # + else_dead = None 11 | # final_dead = self.check_stmts(node.finalbody, deadcode) 12 | # 13 | # deadcode = (handlers_dead and else_dead) or final_dead 14 | # 15 | 16 | try: 17 | length = len(val) 18 | except: 19 | pass 20 | else: 21 | result.set_meta('len', length) -------------------------------------------------------------------------------- /tests/small_samples/3dbbed07d7fec1ba458ca74840ec2f6196114bb49f09193a7704911172b8c04a.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -109,7 +109,7 @@ 6 | # node.__static_value = False 7 | # 8 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.Or): 9 | # - if all(self.static_cnd(n) is False for n in node.values): 10 | # + if all(self.static_cnd(n) is not False for n in node.values): 11 | # node.__static_value = False 12 | # 13 | # if any(self.static_cnd(n) is True for n in node.values): 14 | # 15 | 16 | if level or (level): 17 | sample_type = 'big' 18 | else: 19 | pass -------------------------------------------------------------------------------- /tests/small_samples/93f00cfd350495557ead44e2dd46ac4ce7d913f6e5d86ba1b0d5044cec188438.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -507,7 +507,7 @@ 6 | # "LOAD_GLOBAL", 7 | # "LOAD_FAST", 8 | # "LOAD_DEREF", 9 | # - "LOAD_CLASSDEREF", 10 | # + "XXLOAD_CLASSDEREFXX", 11 | # ): 12 | # typ = ast.Name 13 | # ctx = ast.Load 14 | # 15 | 16 | def __init__(self, out=None, prefix='', columns='time', overwrite=False, color=None, enabled=True, watch_extras=(), replace_watch_extras=None, formatter_class=DefaultFormatter): 17 | 18 | class ConfiguredTracer: 19 | self -------------------------------------------------------------------------------- /tests/small_samples/9a30338a97c5bd67283cb31910c04624714cbf6c19f1382af18efccd443abff9.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -201,7 +201,7 @@ 6 | # index += 4 7 | # 8 | # def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None: 9 | # - if instruction.opname in ("COMPARE_OP", "IS_OP", "CONTAINS_OP") and isinstance( 10 | # + if instruction.opname in ("XXCOMPARE_OPXX", "IS_OP", "CONTAINS_OP") and isinstance( 11 | # node, types_cmp_issue 12 | # ): 13 | # if isinstance(node, types_cmp_issue_fix): 14 | # 15 | 16 | if start_lineno <= node <= end_lineno: 17 | pass -------------------------------------------------------------------------------- /tests/small_samples/d98e27d8963331b58e4e6b84c7580dafde4d9e2980ad4277ce55e6b186113c1d.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -175,7 +175,7 @@ 6 | # for stmt in node.body: 7 | # if isinstance(stmt, ast.ImportFrom): 8 | # if stmt.module == "__future__" and any( 9 | # - "annotations" == alias.name for alias in stmt.names 10 | # + "annotations" != alias.name for alias in stmt.names 11 | # ): 12 | # self.future_annotations = True 13 | # 14 | # 15 | 16 | from __future__ import annotations 17 | 18 | async def get_message(self) -> Message: 19 | pass -------------------------------------------------------------------------------- /tests/small_samples/fc6eb521024986baa84af2634f638e40af090be4aa70ab3c22f3d022e8068228.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -468,7 +468,7 @@ 6 | # return 7 | # 8 | # if node_match(ast.Name, ctx=ast.Load) and inst_match( 9 | # - ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL"), argval=mangled_name(node) 10 | # + ("LOAD_NAME", "XXLOAD_FASTXX", "LOAD_GLOBAL"), argval=mangled_name(node) 11 | # ): 12 | # return 13 | # 14 | # 15 | 16 | class TestMangling: 17 | 18 | def test(self): 19 | try: 20 | raise Exception(10) 21 | except Exception as __e: 22 | __e -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from typing import Optional, Sequence, Union 4 | from executing._pytest_utils import is_pytest_compatible 5 | import _pytest.assertion.rewrite as rewrite 6 | import importlib.machinery 7 | import types 8 | 9 | if not is_pytest_compatible(): 10 | original_find_spec = rewrite.AssertionRewritingHook.find_spec 11 | 12 | 13 | def find_spec( 14 | self, 15 | name: str, 16 | path: Optional[Sequence[Union[str, bytes]]] = None, 17 | target: Optional[types.ModuleType] = None, 18 | ) -> Optional[importlib.machinery.ModuleSpec]: 19 | 20 | if name == "tests.test_main": 21 | return None 22 | return original_find_spec(self, name, path, target) 23 | 24 | 25 | rewrite.AssertionRewritingHook.find_spec = find_spec 26 | -------------------------------------------------------------------------------- /tests/small_samples/227620e1f473c9ac5ccf8328ed28b169c31617f5a4d6302337ce4f83109f745e.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -330,7 +330,7 @@ 6 | # [self.check_stmts(h.body, deadcode) for h in node.handlers] 7 | # ) 8 | # else_dead = self.check_stmts(node.orelse, try_dead) 9 | # - final_dead = self.check_stmts(node.finalbody, deadcode) 10 | # + final_dead = None 11 | # 12 | # deadcode = (handlers_dead and else_dead) or final_dead 13 | # 14 | # 15 | 16 | def exec_ipython_cell(): 17 | try: 18 | shell.ex(traced_file.code) 19 | return self._ipython_cell_value 20 | finally: 21 | callback -------------------------------------------------------------------------------- /tests/small_samples/d06afcef230a0babce66218961fb7dc171e71ae19f194ebf34e85e3c9501eda7.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -496,8 +496,7 @@ 6 | # if ( 7 | # node_match(ast.Name, ctx=ast.Load) 8 | # or ( 9 | # - node_match(ast.Name, ctx=ast.Store) 10 | # - and isinstance(node.parent, ast.AugAssign) 11 | # + node_match(ast.Name, ctx=ast.Store) or isinstance(node.parent, ast.AugAssign) 12 | # ) 13 | # ) and inst_match( 14 | # ("LOAD_NAME", "LOAD_FAST", "LOAD_GLOBAL", "LOAD_DEREF"), argval=mangled_name(node) 15 | # 16 | 17 | result += src[start_pos] -------------------------------------------------------------------------------- /tests/small_samples/c1a8b67677b66314b60f56743bfa80e17c35cf4f5c4ab2c890c693969db41fdc.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -102,7 +102,7 @@ 6 | # node.__static_value = node.orelse.__static_value 7 | # 8 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): 9 | # - if all(self.static_cnd(n) is True for n in node.values): 10 | # + if all(self.static_cnd(n) is not True for n in node.values): 11 | # node.__static_value = True 12 | # 13 | # if any(self.static_cnd(n) is False for n in node.values): 14 | # 15 | 16 | if exc_value and node: 17 | pass 18 | else: 19 | NodeValue -------------------------------------------------------------------------------- /tests/small_samples/45a20d9fefe2db8c5fbc91ad26e8e9dce0667e126dab5e34fd6825c085776be5.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -230,7 +230,7 @@ 6 | # 7 | # else: 8 | # # Comprehension and generators get not fixed for now. 9 | # - raise KnownIssue("chain comparison inside %s can not be fixed" % (node)) 10 | # + raise KnownIssue("chain comparison inside %s can not be fixed" / (node)) 11 | # 12 | # if isinstance(node, ast.Assert): 13 | # # pytest assigns the position of the assertion to all expressions of the rewritten assertion. 14 | # 15 | 16 | [r for r in results if start <= r <= end] -------------------------------------------------------------------------------- /tests/small_samples/780d1e5c82a0d4c02798ef5d5f2f8895334a242128f7a7a300f44cf2036b8dc4.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -191,8 +191,7 @@ 6 | # index += 2 7 | # 8 | # if ( 9 | # - self.opname(index).startswith("STORE_") 10 | # - and self.find_node(index) == node_func 11 | # + self.opname(index).startswith("STORE_") or self.find_node(index) == node_func 12 | # ): 13 | # self.result = node_func 14 | # self.decorator = node 15 | # 16 | 17 | @lru_cache() 18 | def compile(self, source, filename, flags=0): 19 | pass -------------------------------------------------------------------------------- /tests/small_samples/0fe16ec438396e003734858ad326d907e514273262547abaee0cd15e6eb7083e.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -332,7 +332,7 @@ 6 | # else_dead = self.check_stmts(node.orelse, try_dead) 7 | # final_dead = self.check_stmts(node.finalbody, deadcode) 8 | # 9 | # - deadcode = (handlers_dead and else_dead) or final_dead 10 | # + deadcode = (handlers_dead and else_dead) and final_dead 11 | # 12 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): 13 | # dead_op = deadcode 14 | # 15 | 16 | def __getattr__(): 17 | try: 18 | return self[name] 19 | except KeyError: 20 | return '' 21 | name -------------------------------------------------------------------------------- /tests/small_samples/4851dc1b626a95e97dbe0c53f96099d165b755dd1bd552c6ca771f7bca6d30f5.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -201,7 +201,7 @@ 6 | # index += 4 7 | # 8 | # def known_issues(self, node: EnhancedAST, instruction: dis.Instruction) -> None: 9 | # - if instruction.opname in ("COMPARE_OP", "IS_OP", "CONTAINS_OP") and isinstance( 10 | # + if instruction.opname in ("COMPARE_OP", "XXIS_OPXX", "CONTAINS_OP") and isinstance( 11 | # node, types_cmp_issue 12 | # ): 13 | # if isinstance(node, types_cmp_issue_fix): 14 | # 15 | 16 | if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in 1 is 1 is not 1: 17 | pass -------------------------------------------------------------------------------- /tests/small_samples/6602443ba655e0e139cfb923d581f8da91071d631a42f81253c1e47fdc952da3.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -440,7 +440,7 @@ 6 | # return 7 | # 8 | # if ( 9 | # - inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "STORE_GLOBAL")) 10 | # + inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "XXSTORE_GLOBALXX")) 11 | # and node_match((ast.Import, ast.ImportFrom)) 12 | # and any(mangled_name(cast(EnhancedAST, alias)) == instruction.argval for alias in cast(ast.Import, node).names) 13 | # ): 14 | # 15 | 16 | import keyring 17 | 18 | def get_keyring_auth(): 19 | global keyring -------------------------------------------------------------------------------- /tests/small_samples/4fb0f58b18c1b1484aec340e1a7ab7cdc87185a067ee37a73f05eb1a1d08bb11.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -242,7 +242,7 @@ 6 | # # TODO: investigate 7 | # raise KnownIssue("pattern matching ranges seems to be wrong") 8 | # 9 | # - if instruction.opname == "STORE_NAME" and instruction.argval == "__classcell__": 10 | # + if instruction.opname == "STORE_NAME" or instruction.argval == "__classcell__": 11 | # # handle stores to __classcell__ as KnownIssue, 12 | # # because they get complicated if they are used in `if` or `for` loops 13 | # # example: 14 | # 15 | 16 | CodeInfo = namedtuple -------------------------------------------------------------------------------- /tests/small_samples/9b3db37076d3c7c76bdfd9badcc70d8047584433e1eea89f45014453d58bbc43.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -177,7 +177,7 @@ 6 | # if stmt.module == "__future__" and any( 7 | # "annotations" == alias.name for alias in stmt.names 8 | # ): 9 | # - self.future_annotations = True 10 | # + self.future_annotations = None 11 | # 12 | # self.check_stmts(node.body, deadcode) 13 | # elif isinstance(node, (ast.With, ast.AsyncWith)): 14 | # 15 | 16 | from __future__ import annotations, with_statement 17 | 18 | async def _start(self: Cloud) -> Cloud[IsAsynchronous]: 19 | pass -------------------------------------------------------------------------------- /tests/small_samples/0194cd769109c110d50905631c5793818736ff3835dd0a5ef97ed2f34cd65892.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -112,7 +112,7 @@ 6 | # if all(self.static_cnd(n) is False for n in node.values): 7 | # node.__static_value = False 8 | # 9 | # - if any(self.static_cnd(n) is True for n in node.values): 10 | # + if any(self.static_cnd(n) is not True for n in node.values): 11 | # node.__static_value = True 12 | # 13 | # except AttributeError as e: 14 | # 15 | 16 | def after_stmt(self, node, frame, exc_value, exc_traceback, exc_node): 17 | if frame or _tracing_recursively: 18 | return None 19 | exc_value -------------------------------------------------------------------------------- /tests/small_samples/7fff707ddf87544616f198155d98e215700b261e39fda3844940ec2aab5a0610.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -332,7 +332,7 @@ 6 | # else_dead = self.check_stmts(node.orelse, try_dead) 7 | # final_dead = self.check_stmts(node.finalbody, deadcode) 8 | # 9 | # - deadcode = (handlers_dead and else_dead) or final_dead 10 | # + deadcode = (handlers_dead or else_dead) or final_dead 11 | # 12 | # elif isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): 13 | # dead_op = deadcode 14 | # 15 | 16 | def for_filename(): 17 | try: 18 | return source_cache[filename] 19 | except KeyError: 20 | pass 21 | linecache -------------------------------------------------------------------------------- /tests/small_samples/a33f71955b90ec91ef184fccf593e537aa583db12b67d9130917a4349701eaa2.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -414,7 +414,7 @@ 6 | # return 7 | # 8 | # if ( 9 | # - inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "STORE_GLOBAL")) 10 | # + inst_match(("STORE_NAME", "XXSTORE_FASTXX", "STORE_DEREF", "STORE_GLOBAL")) 11 | # and node_match((ast.Import, ast.ImportFrom)) 12 | # and any(mangled_name(cast(EnhancedAST, alias)) == instruction.argval for alias in cast(ast.Import, node).names) 13 | # ): 14 | # 15 | 16 | def exec_ipython_cell(self, source, callback): 17 | from IPython import get_ipython -------------------------------------------------------------------------------- /tests/small_samples/e8a94f0a994ea84627b7cbc9132cf519ccaa6d756f8cb3458cf019e672a510a4.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -219,7 +219,7 @@ 6 | # if isinstance(n, ast.Compare) and len(n.ops) > 1 7 | # ] 8 | # 9 | # - assert_(comparisons, "expected at least one comparison") 10 | # + assert_(comparisons, "XXexpected at least one comparisonXX") 11 | # 12 | # if len(comparisons) == 1: 13 | # node = self.result = cast(EnhancedAST, comparisons[0]) 14 | # 15 | 16 | def _get_windows_argv(): 17 | try: 18 | argv = [argv_unicode[i] for i in range(0, argc.value)] 19 | finally: 20 | del argv_unicode -------------------------------------------------------------------------------- /tests/small_samples/736316309812e0ca8306cb7eec4c20d75f8e3c45cab3cd46cf4370524ada6823.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -399,7 +399,7 @@ 6 | # if inst_match(("BEFORE_WITH","WITH_EXCEPT_START")) and node_match(ast.With): 7 | # return 8 | # 9 | # - if inst_match(("STORE_NAME", "STORE_GLOBAL"), argval="__doc__") and node_match( 10 | # + if inst_match(("XXSTORE_NAMEXX", "STORE_GLOBAL"), argval="__doc__") and node_match( 11 | # ast.Constant 12 | # ): 13 | # # store docstrings 14 | # 15 | 16 | class BirdsEye: 17 | """ 18 | Decorate functions with an instance of this class to debug them, 19 | or just use the existing instance `eye`. 20 | """ -------------------------------------------------------------------------------- /tests/small_samples/91f5b684f56b415a61d211027904e023f2371952602a8d71c17416d8fa3ceed7.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -242,7 +242,7 @@ 6 | # # TODO: investigate 7 | # raise KnownIssue("pattern matching ranges seems to be wrong") 8 | # 9 | # - if instruction.opname == "STORE_NAME" and instruction.argval == "__classcell__": 10 | # + if instruction.opname != "STORE_NAME" and instruction.argval == "__classcell__": 11 | # # handle stores to __classcell__ as KnownIssue, 12 | # # because they get complicated if they are used in `if` or `for` loops 13 | # # example: 14 | # 15 | 16 | class BirdsEye: 17 | 18 | def __init__(self, db_uri=None, num_samples=None): 19 | super -------------------------------------------------------------------------------- /tests/small_samples/ea94f24a1d9b57c7a7d62c428082790caae2fa16429db2e58b2f4addb67a1964.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -414,7 +414,7 @@ 6 | # return 7 | # 8 | # if ( 9 | # - inst_match(("STORE_NAME", "STORE_FAST", "STORE_DEREF", "STORE_GLOBAL")) 10 | # + inst_match(("STORE_NAME", "STORE_FAST", "XXSTORE_DEREFXX", "STORE_GLOBAL")) 11 | # and node_match((ast.Import, ast.ImportFrom)) 12 | # and any(mangled_name(cast(EnhancedAST, alias)) == instruction.argval for alias in cast(ast.Import, node).names) 13 | # ): 14 | # 15 | 16 | def read_source_file(filename): 17 | from lib2to3.pgen2.tokenize import cookie_re 18 | with open_with_encoding_check as f: 19 | [cookie_re for i, line in enumerate] -------------------------------------------------------------------------------- /executing/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get information about what a frame is currently doing. Typical usage: 3 | 4 | import executing 5 | 6 | node = executing.Source.executing(frame).node 7 | # node will be an AST node or None 8 | """ 9 | 10 | from collections import namedtuple 11 | _VersionInfo = namedtuple('_VersionInfo', ('major', 'minor', 'micro')) 12 | from .executing import Source, Executing, only, NotOneValueFound, cache, future_flags 13 | 14 | from ._pytest_utils import is_pytest_compatible 15 | 16 | try: 17 | from .version import __version__ # type: ignore[import] 18 | if "dev" in __version__: 19 | raise ValueError 20 | except Exception: 21 | # version.py is auto-generated with the git tag when building 22 | __version__ = "???" 23 | __version_info__ = _VersionInfo(-1, -1, -1) 24 | else: 25 | __version_info__ = _VersionInfo(*map(int, __version__.split('.'))) 26 | 27 | 28 | __all__ = ["Source","is_pytest_compatible"] 29 | -------------------------------------------------------------------------------- /tests/test_ipython.py: -------------------------------------------------------------------------------- 1 | import subprocess as sp 2 | import sys 3 | import pytest 4 | 5 | 6 | def run(input): 7 | p = sp.run( 8 | [sys.executable, "-m", "IPython", "--colors=nocolor", "--simple-prompt"], 9 | input=input.encode("utf8"), 10 | stdout=sp.PIPE, 11 | ) 12 | output = p.stdout.decode("utf8") 13 | print(output) 14 | return output 15 | 16 | 17 | test_function_code = """ 18 | from executing import Source 19 | import inspect 20 | import ast 21 | 22 | def test(): 23 | frame = inspect.currentframe() 24 | ex = Source.executing(frame.f_back) 25 | print(ex.node) 26 | if not isinstance(ex.node,ast.Call): 27 | print("test failure") 28 | if not ex.node.func.id=="test": 29 | print("test failure") 30 | 31 | """ 32 | 33 | 34 | def test_one_lookup(): 35 | p = run(test_function_code + "test()") 36 | assert "test failure" not in p 37 | 38 | 39 | def test_two_statement_lookups(): 40 | p = run(test_function_code + "test();test()") 41 | assert "test failure" in p 42 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = {mypy-,}{py38,py39,py310,py311},py312,py313,py314-dev,pypy35,pypy36 3 | 4 | [testenv] 5 | commands = 6 | pytest tests {posargs} 7 | extras = tests 8 | passenv = 9 | FIX_EXECUTING_TESTS 10 | ADD_EXECUTING_TESTS 11 | EXECUTING_SLOW_TESTS 12 | 13 | [testenv:generate_small_sample-py{38,39,310,311,312,313-dev}] 14 | extras = tests 15 | deps = pysource-minimize 16 | commands = 17 | python -m tests.generate_small_sample {posargs} 18 | passenv = 19 | MUTMUT_HEADER 20 | 21 | [testenv:mutmut] 22 | # mutmut had problems on other python versions 23 | # that is the reason it runs in its own environment 24 | basepython=python3.10 25 | deps= 26 | mutmut 27 | commands= 28 | python tests/mutmut_workflow.py 29 | 30 | 31 | [testenv:mypy-py{35,36,37,38,39,310}] 32 | deps= 33 | mypy==0.910 34 | commands= 35 | python -m mypy executing --exclude=executing/_position_node_finder.py 36 | 37 | 38 | [testenv:mypy-py{311}] 39 | deps= 40 | mypy==0.971 41 | commands= 42 | python -m mypy executing 43 | -------------------------------------------------------------------------------- /tests/small_samples/2f31d64a74ed18026d7000094cefc6e5cace573b0df257f23299ed7bb21a3264.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -370,7 +370,7 @@ 6 | # 7 | # if ( 8 | # ( 9 | # - inst_match("LOAD_METHOD", argval="join") 10 | # + inst_match("XXLOAD_METHODXX", argval="join") 11 | # or inst_match(("CALL", "BUILD_STRING")) 12 | # ) 13 | # and node_match(ast.BinOp, left=ast.Constant, op=ast.Mod) 14 | # 15 | 16 | 'Configuration\n - files_or_dirs: %s\n - verbosity: %s\n - tests: %s\n - port: %s\n - files_to_tests: %s\n - jobs: %s\n - split_jobs: %s\n\n - include_files: %s\n - include_tests: %s\n\n - exclude_files: %s\n - exclude_tests: %s\n\n - coverage_output_dir: %s\n - coverage_include_dir: %s\n - coverage_output_file: %s\n\n - django: %s\n' % (self, self, self, self, self, self, self, self, self, self, self, self, self, self, self) -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alex Hall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/small_samples/206e0609ff0589a0a32422ee902f09156af91746e27157c32c9595d12072f92a.py: -------------------------------------------------------------------------------- 1 | f'\n\n subroutine {fprefix}_input_{fsuffix}(c, o, n)\n character*{clength}, intent(in) :: c\n integer n\n !f2py integer, depend(c), intent(hide) :: n = slen(c)\n integer*1, dimension(n) :: o\n !f2py intent(out) o\n o = transfer(c, o)\n end subroutine {fprefix}_input_{fsuffix}\n\n subroutine {fprefix}_output_{fsuffix}(c, o, n)\n character*{clength}, intent(out) :: c\n integer n\n integer*1, dimension(n), intent(in) :: o\n !f2py integer, depend(o), intent(hide) :: n = len(o)\n c = transfer(o, c)\n end subroutine {fprefix}_output_{fsuffix}\n\n subroutine {fprefix}_array_input_{fsuffix}(c, o, m, n)\n integer m, i, n\n character*{clength}, intent(in), dimension(m) :: c\n !f2py integer, depend(c), intent(hide) :: m = len(c)\n !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)\n integer*1, dimension(m, n), intent(out) :: o\n do i=1,m\n o(i, :) = transfer(c(i), o(i, :))\n end do\n end subroutine {fprefix}_array_input_{fsuffix}\n\n subroutine ' -------------------------------------------------------------------------------- /tests/small_samples/2ab181c0e2d4978b1dc22cb8d9a4ed3e2bc6df02267f9766dfe1e8ee7924ff7c.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -36,27 +36,7 @@ 6 | # def __init__(self): 7 | # self.future_annotations = False 8 | # 9 | # - operator_map = { 10 | # - # binary 11 | # - ast.Add: operator.add, 12 | # - ast.Sub: operator.sub, 13 | # - ast.Mult: operator.mul, 14 | # - ast.Div: operator.truediv, 15 | # - ast.FloorDiv: operator.floordiv, 16 | # - ast.Mod: operator.mod, 17 | # - ast.Pow: operator.pow, 18 | # - ast.LShift: operator.lshift, 19 | # - ast.RShift: operator.rshift, 20 | # - ast.BitOr: operator.or_, 21 | # - ast.BitXor: operator.xor, 22 | # - ast.BitAnd: operator.and_, 23 | # - ast.MatMult: operator.matmul, 24 | # - # unary 25 | # - ast.UAdd: operator.pos, 26 | # - ast.USub: operator.neg, 27 | # - ast.Not: operator.not_, 28 | # - ast.Invert: operator.invert, 29 | # - } 30 | # + operator_map = None 31 | # 32 | # def annotate_static_values(self, node): 33 | # for n in ast.iter_child_nodes(node): 34 | # 35 | 36 | if not 'a': 37 | print -------------------------------------------------------------------------------- /tests/small_samples/cb33a25d03eeb972e05ef6585afab710a204477850e8b0f9bad0482f9b4364b2.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- executing/_position_node_finder.py 4 | # +++ executing/_position_node_finder.py 5 | # @@ -425,7 +425,7 @@ 6 | # if inst_match(("BEFORE_WITH","WITH_EXCEPT_START")) and node_match(ast.With): 7 | # return 8 | # 9 | # - if inst_match(("STORE_NAME", "STORE_GLOBAL"), argval="__doc__") and node_match( 10 | # + if inst_match(("STORE_NAME", "XXSTORE_GLOBALXX"), argval="__doc__") and node_match( 11 | # ast.Constant 12 | # ): 13 | # # store docstrings 14 | # 15 | 16 | """ 17 | The ``codes`` object defines a mapping from common names for HTTP statuses 18 | to their numerical codes, accessible either as attributes or as dictionary 19 | items. 20 | 21 | Example:: 22 | 23 | >>> import requests 24 | >>> requests.codes['temporary_redirect'] 25 | 307 26 | >>> requests.codes.teapot 27 | 418 28 | >>> requests.codes['\\o/'] 29 | 200 30 | 31 | Some codes have multiple names, and both upper- and lower-case versions of 32 | the names are allowed. For example, ``codes.ok``, ``codes.OK``, and 33 | ``codes.okay`` all correspond to the HTTP status code 200. 34 | """ 35 | global __doc__ -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = executing 3 | author = Alex Hall 4 | author_email = alex.mojaki@gmail.com 5 | license = MIT 6 | description = Get the currently executing AST node of a frame, and other information 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/alexmojaki/executing 10 | classifiers = 11 | License :: OSI Approved :: MIT License 12 | Programming Language :: Python 13 | Programming Language :: Python :: 3 14 | Programming Language :: Python :: 3.8 15 | Programming Language :: Python :: 3.9 16 | Programming Language :: Python :: 3.10 17 | Programming Language :: Python :: 3.11 18 | Programming Language :: Python :: 3.12 19 | Programming Language :: Python :: 3.13 20 | Programming Language :: Python :: 3.14 21 | 22 | [options] 23 | packages = executing 24 | zip_safe = False 25 | include_package_data = True 26 | setup_requires = setuptools; setuptools_scm[toml] 27 | python_requires = >=3.8 28 | 29 | [options.extras_require] 30 | tests= 31 | asttokens>=2.1.0 32 | ipython 33 | pytest 34 | coverage 35 | coverage-enable-subprocess 36 | littleutils 37 | rich; python_version >='3.11' 38 | 39 | [options.package_data] 40 | executing = py.typed 41 | 42 | [coverage:run] 43 | relative_files = True 44 | include = executing/executing.py 45 | parallel = true 46 | branch = true 47 | 48 | [bdist_wheel] 49 | universal=1 50 | -------------------------------------------------------------------------------- /development.md: -------------------------------------------------------------------------------- 1 | # Development workflows & tools 2 | 3 | executing can check arbitrary python source files: 4 | 5 | - files in `tests/small_samples` are checked by default 6 | - files in `tests/samples` are checked if the `EXECUTING_SLOW_TESTS=1` is set 7 | 8 | run slow tests in parallel: 9 | ~~~ sh 10 | env EXECUTING_SLOW_TESTS=1 tox -p 11 | ~~~ 12 | 13 | pytest parmeters can be passed to tox. 14 | 15 | ~~~ sh 16 | env EXECUTING_SLOW_TESTS=1 tox -e py311 -- --sw 17 | ~~~ 18 | 19 | 20 | ## generate_small_sample 21 | 22 | `tox -e genererate_small_sample` can be used to: 23 | 24 | 1. find python source code which triggers a bug in executing 25 | 2. minimize this source code 26 | 3. stores this samples in `tests/small_samples` 27 | 28 | Usage: 29 | 30 | minimize failures in `tests/samples` 31 | ~~~ sh 32 | tox -e generate_small_sample-py311 33 | ~~~ 34 | 35 | search other project for potential problems 36 | ~~~ sh 37 | tox -e generate_small_sample-py311 -- ~/path/to/python-3.11.0/ 38 | ~~~ 39 | 40 | ## mutmut 41 | 42 | [mutmut](https://mutmut.readthedocs.io/en/latest/) can be used to mutation testing 43 | 44 | The weak points in the tests which are discovered by mutmut are normally fixed by developer (creating new test cases). 45 | 46 | But "tox -e mutmut" combines `generate_small_sample` and mutmut. 47 | Tests are generated automatically for issues which are discovered by mutmut. 48 | 49 | Usage: 50 | ``` 51 | tox -e mutmut 52 | ``` 53 | 54 | You should have a clean git working directory, because mutmut changes files. 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /executing/version.py 2 | 3 | # Created by .ignore support plugin (hsz.mobi) 4 | ### Python template 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | .hypothesis/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | .static_storage/ 60 | .media/ 61 | local_settings.py 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | .env 90 | .venv 91 | env/ 92 | venv*/ 93 | ENV/ 94 | env.bak/ 95 | venv.bak/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | .dmypy.json 110 | 111 | # VSCode 112 | .vscode/ 113 | 114 | # mutmut 115 | .mutmut-cache 116 | 117 | # testing tools 118 | /done.db 119 | /tests/last_samples/ 120 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-24.04 13 | strategy: 14 | matrix: 15 | python-version: [3.8, 3.9, '3.10', '3.11', '3.12' ,'3.13' ,3.14-dev] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Install dependencies 24 | run: | 25 | python --version 26 | pip install -U pip 27 | pip install --upgrade coveralls setuptools setuptools_scm pep517 .[tests] 28 | pip install . 29 | - name: Mypy testing (<3.11) 30 | run: | 31 | pip install mypy==0.910 32 | python -m mypy executing --exclude=executing/_position_node_finder.py 33 | # fromJson because https://github.community/t/passing-an-array-literal-to-contains-function-causes-syntax-error/17213/3 34 | if: ${{ !contains(fromJson('["pypy-3.6", "3.11","3.12","3.13","3.14-dev"]'), matrix.python-version) }} 35 | # pypy < 3.8 very doesn't work 36 | - name: Mypy testing (3.11) 37 | run: | 38 | pip install mypy==0.971 39 | python -m mypy executing 40 | # fromJson because https://github.community/t/passing-an-array-literal-to-contains-function-causes-syntax-error/17213/3 41 | # TODO: enable typechecking for 3.12 42 | if: ${{ contains(fromJson('["3.11"]'), matrix.python-version) }} 43 | # only >=3.11 use _position_node_finder.py 44 | - name: Test 45 | env: 46 | EXECUTING_SLOW_TESTS: 1 47 | run: | 48 | # COVERAGE_PROCESS_START defines the path to the coverage config for coverage-enable-subprocess 49 | export COVERAGE_PROCESS_START=${GITHUB_WORKSPACE}/setup.cfg 50 | coverage run -m pytest tests 51 | # combine the coverage of all subprocesses 52 | coverage combine 53 | coverage report -m 54 | - name: Coveralls Python 55 | uses: AndreMiras/coveralls-python-action@v20201129 56 | with: 57 | parallel: true 58 | flag-name: test-${{ matrix.python-version }} 59 | 60 | coveralls_finish: 61 | needs: test 62 | runs-on: ubuntu-latest 63 | steps: 64 | - name: Coveralls Finished 65 | uses: AndreMiras/coveralls-python-action@v20201129 66 | with: 67 | parallel-finished: true 68 | -------------------------------------------------------------------------------- /tests/small_samples/6d981a27f4f4672f186cb498c6c012ac2c6168dfd631e7fff5602e9e5586f00e.py: -------------------------------------------------------------------------------- 1 | # This sample was generated for the following code mutation detected by mutmut: 2 | # 3 | # --- tests/deadcode.py 4 | # +++ tests/deadcode.py 5 | # @@ -273,6 +273,36 @@ 6 | # 7 | # else_is_dead = self.check_stmts(node.orelse, deadcode) 8 | # 9 | # + if else_is_dead or not contains_break(node.body): 10 | # + # for a in l: 11 | # + # something() 12 | # + # else: 13 | # + # return None 14 | # + # deadcode() 15 | # + deadcode = True 16 | # + 17 | # + elif isinstance(node, ast.IfExp): 18 | # + 19 | # + test_value = self.static_value(node.test, deadcode) 20 | # + 21 | # + self.walk_deadcode( 22 | # + node.body, deadcode or (test_value is False) 23 | # + ) 24 | # + 25 | # + self.walk_deadcode( 26 | # + node.orelse, deadcode or (test_value is True) 27 | # + ) 28 | # + 29 | # + elif isinstance(node, (ast.While)): 30 | # + cnd = self.static_value(node.test, deadcode) 31 | # + 32 | # + self.check_stmts(node.body, deadcode or cnd is False) 33 | # + else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 34 | # + 35 | # + if cnd is True and not contains_break(node): 36 | # + # while True: ... no break 37 | # + deadcode = True 38 | # + 39 | # if else_is_dead and not contains_break(node.body): 40 | # # for a in l: 41 | # # something() 42 | # @@ -281,36 +311,6 @@ 43 | # # deadcode() 44 | # deadcode = True 45 | # 46 | # - elif isinstance(node, ast.IfExp): 47 | # - 48 | # - test_value = self.static_value(node.test, deadcode) 49 | # - 50 | # - self.walk_deadcode( 51 | # - node.body, deadcode or (test_value is False) 52 | # - ) 53 | # - 54 | # - self.walk_deadcode( 55 | # - node.orelse, deadcode or (test_value is True) 56 | # - ) 57 | # - 58 | # - elif isinstance(node, (ast.While)): 59 | # - cnd = self.static_value(node.test, deadcode) 60 | # - 61 | # - self.check_stmts(node.body, deadcode or cnd is False) 62 | # - else_is_dead = self.check_stmts(node.orelse, deadcode or cnd is True) 63 | # - 64 | # - if cnd is True and not contains_break(node): 65 | # - # while True: ... no break 66 | # - deadcode = True 67 | # - 68 | # - if else_is_dead and not contains_break(node.body): 69 | # - # for a in l: 70 | # - # something() 71 | # - # else: 72 | # - # return None 73 | # - # deadcode() 74 | # - deadcode = True 75 | # - 76 | # elif isinstance(node, (ast.Try, ast.TryStar)): 77 | # try_dead = self.check_stmts(node.body, deadcode) 78 | # 79 | # 80 | 81 | for loop_node in node: 82 | pass 83 | value -------------------------------------------------------------------------------- /tests/mutmut_workflow.py: -------------------------------------------------------------------------------- 1 | import subprocess as sp 2 | import os 3 | 4 | import shelve 5 | import time 6 | 7 | files = ["executing/_position_node_finder.py", "tests/deadcode.py"] 8 | 9 | py_version = "py311" 10 | 11 | 12 | def mutmut_run(num: str | None = None): 13 | cmd = [ 14 | "mutmut", 15 | "run", 16 | "--paths-to-mutate", 17 | ",".join(files), 18 | "--runner", 19 | f"tox -e {py_version} -- --ff", 20 | *([num] if num is not None else []), 21 | ] 22 | print(">", *cmd) 23 | sp.run(cmd) 24 | 25 | 26 | def survived() -> set[str]: 27 | """ 28 | set of all the ids which survived 29 | """ 30 | nums = sp.check_output(["mutmut", "result-ids", "survived"]).decode().split() 31 | if not all(num.isnumeric() for num in nums): 32 | return set() 33 | return set(nums) 34 | 35 | 36 | def main(): 37 | # Mutmut is not really build for this kind of integration. 38 | # This is the reason for some weird code here. 39 | 40 | # make sure that there are no known bugs 41 | sp.check_call(["git", "checkout", *files]) 42 | sp.check_call(["tox", "-e", py_version]) 43 | 44 | # done.db contains all mutmut ids which have been already tried to fix. 45 | # Useful if this is run multiple times 46 | with shelve.open("done.db") as done: 47 | while True: 48 | todo = survived() - done.keys() 49 | 50 | if not todo: 51 | # mutmut has to run without id first 52 | # It also only checks for the first 100 untested mutations, 53 | # `not todo` does not imply that there is nothing more to test 54 | mutmut_run() 55 | todo = survived() - done.keys() 56 | 57 | if not todo: 58 | break 59 | 60 | print("survived mutations todo:", todo) 61 | for num in todo: 62 | # make sure to base this run on clean files 63 | sp.check_call(["git", "checkout", *files]) 64 | 65 | # applies the mutated state to the files and runs the mutation 66 | mutmut_run(num) 67 | 68 | # skip if the mutation has not survived (is covered by some tests) 69 | if num not in survived(): 70 | continue 71 | 72 | header_diff = sp.check_output(["mutmut", "show", num]).decode() 73 | 74 | sp.check_call(["mutmut", "apply", num]) 75 | 76 | # generate a sample for the mutmut run 77 | sp.check_call( 78 | ["tox", "-e", f"generate_small_sample-{py_version}"], 79 | env=os.environ | {"MUTMUT_HEADER": header_diff}, 80 | ) 81 | 82 | sp.check_call(["git", "checkout", *files]) 83 | 84 | # The normal tests should pass. 85 | # There are some cases where generate_small_sample found a different bug 86 | # and the tests failed. 87 | # this script will fail in this case and the bug should be fixed by the developer 88 | result = sp.run(["tox", "-e", py_version]) 89 | if result.returncode != 0: 90 | print( 91 | "generate_small_sample found a different bug that should be fixed by the developer" 92 | ) 93 | exit(1) 94 | 95 | done[num] = True 96 | 97 | 98 | if __name__ == "__main__": 99 | main() 100 | -------------------------------------------------------------------------------- /tests/analyse.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pathlib 3 | import dis 4 | import types 5 | import inspect 6 | 7 | from executing import Source 8 | import executing 9 | 10 | executing.executing.TESTING = 1 11 | 12 | from rich import print as rprint 13 | import rich 14 | 15 | 16 | class Frame: 17 | pass 18 | 19 | 20 | if len(sys.argv) <= 1 or sys.argv[1] in ("--help", "-h"): 21 | print( 22 | """ 23 | analyse.py [line | first_line:last_line] 24 | 25 | Analyses a range in the given source in the specified range 26 | and maps every bytecode to the node found by executing. 27 | """ 28 | ) 29 | sys.exit(0) 30 | 31 | filename = pathlib.Path(sys.argv[1]) 32 | 33 | if len(sys.argv)<3: 34 | start=0 35 | end=100 36 | elif ":" in sys.argv[2]: 37 | start, end = sys.argv[2].split(":") 38 | start = int(start) 39 | end = int(end) 40 | else: 41 | start = end = int(sys.argv[2]) 42 | 43 | 44 | code = filename.read_text() 45 | 46 | 47 | def inspect_opcode(bytecode, index, lineno): 48 | frame = Frame() 49 | frame.f_lasti = index 50 | frame.f_code = bytecode 51 | frame.f_globals = globals() 52 | frame.f_lineno = lineno 53 | source = Source.for_frame(frame) 54 | 55 | try: 56 | ex = Source.executing(frame) 57 | except RuntimeError: 58 | raise 59 | except Exception as e: 60 | return "[red]" + type(e).__name__ + ": " + str(e).split("\n")[0] 61 | 62 | result = "[green]" + type(ex.node).__name__ 63 | if hasattr(ex.node, "name"): 64 | result += "(" + str(ex.node.name) + ")" 65 | elif hasattr(ex.node, "id"): 66 | result += "(" + ex.node.id + ")" 67 | elif hasattr(ex.node, "attr"): 68 | result += "(." + ex.node.attr + ")" 69 | elif hasattr(ex.node, "value"): 70 | result += f"({ex.node.value})" 71 | 72 | if ex.decorator: 73 | result += " @%s" % ex.decorator 74 | return result 75 | 76 | 77 | 78 | import rich.syntax 79 | from rich.console import Console 80 | from rich.table import Table, Column 81 | from rich.highlighter import ReprHighlighter 82 | 83 | console = Console() 84 | 85 | console.print( 86 | rich.syntax.Syntax(code, "python", line_numbers=True, line_range=(start, end)) 87 | ) 88 | 89 | 90 | print("all bytecodes in this range:") 91 | 92 | bc = compile(code, filename, "exec") 93 | 94 | 95 | def inspect(bc): 96 | first = True 97 | table = Table( 98 | title=bc.co_name + ":", 99 | box=None, 100 | title_style="blue", 101 | title_justify="left", 102 | ) 103 | 104 | table.add_column("offset", justify="right") 105 | table.add_column("start") 106 | table.add_column("end") 107 | table.add_column("instruction") 108 | table.add_column("ast-node") 109 | 110 | 111 | highlighter=ReprHighlighter() 112 | 113 | for i in dis.get_instructions(bc, show_caches=True): 114 | 115 | if ( 116 | i.positions.lineno is None 117 | or i.positions.lineno <= end 118 | and start <= i.positions.end_lineno 119 | ): 120 | if first: 121 | first = False 122 | 123 | 124 | ex = inspect_opcode(bc, i.offset, i.positions.lineno) 125 | 126 | table.add_row( 127 | str(i.offset), 128 | "%s:%s" % (i.positions.lineno, i.positions.col_offset), 129 | "%s:%s" % (i.positions.end_lineno, i.positions.end_col_offset), 130 | highlighter("%s(%s)" % (i.opname, i.argrepr)), 131 | ex, 132 | style="on grey19" if i.opname=="CACHE" else "on grey30" 133 | #**({"style":"on white" } if i.opname=="CACHE" else {}) 134 | ) 135 | 136 | if first == False: 137 | console.print() 138 | console.print(table) 139 | 140 | for const in bc.co_consts: 141 | if isinstance(const, types.CodeType): 142 | inspect(const) 143 | 144 | 145 | inspect(bc) 146 | -------------------------------------------------------------------------------- /tests/samples/import_hook.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | from importlib.util import spec_from_loader 4 | import ast 5 | 6 | 7 | # This is based on the MacroPy import hook 8 | # https://github.com/lihaoyi/macropy/blob/46ee500b877d5a32b17391bb8122c09b15a1826a/macropy/core/import_hooks.py 9 | 10 | class BirdsEyeLoader: 11 | 12 | def __init__(self, spec, source, deep): 13 | self._spec = spec 14 | self.source = source 15 | self.deep = deep 16 | 17 | def create_module(self, spec): 18 | pass 19 | 20 | def exec_module(self, module): 21 | from birdseye.bird import eye 22 | eye.exec_string( 23 | source=self.source, 24 | filename=self._spec.origin, 25 | globs=module.__dict__, 26 | locs=module.__dict__, 27 | deep=self.deep, 28 | ) 29 | 30 | def get_filename(self, fullname): 31 | return self._spec.loader.get_filename(fullname) 32 | 33 | def is_package(self, fullname): 34 | return self._spec.loader.is_package(fullname) 35 | 36 | 37 | class BirdsEyeFinder(object): 38 | """Loads a module and looks for tracing inside, only providing a loader 39 | if it finds some. 40 | """ 41 | 42 | def _find_plain_spec(self, fullname, path, target): 43 | """Try to find the original module using all the 44 | remaining meta_path finders.""" 45 | spec = None 46 | for finder in sys.meta_path: 47 | # when testing with pytest, it installs a finder that for 48 | # some yet unknown reasons makes birdseye 49 | # fail. For now it will just avoid using it and pass to 50 | # the next one 51 | if finder is self or 'pytest' in finder.__module__: 52 | continue 53 | if hasattr(finder, 'find_spec'): 54 | spec = finder.find_spec(fullname, path, target=target) 55 | elif hasattr(finder, 'load_module'): 56 | spec = spec_from_loader(fullname, finder) 57 | 58 | if spec is not None and spec.origin != 'builtin': 59 | return spec 60 | 61 | def find_spec(self, fullname, path, target=None): 62 | spec = self._find_plain_spec(fullname, path, target) 63 | if spec is None or not (hasattr(spec.loader, 'get_source') and 64 | callable(spec.loader.get_source)): # noqa: E128 65 | if fullname != 'org': 66 | # stdlib pickle.py at line 94 contains a ``from 67 | # org.python.core for Jython which is always failing, 68 | # of course 69 | logging.debug('Failed finding spec for %s', fullname) 70 | return 71 | 72 | try: 73 | source = spec.loader.get_source(fullname) 74 | except ImportError: 75 | logging.debug('Loader for %s was unable to find the sources', 76 | fullname) 77 | return 78 | except Exception: 79 | logging.exception('Loader for %s raised an error', fullname) 80 | return 81 | 82 | if not source or 'birdseye' not in source: 83 | return 84 | 85 | deep, trace_stmt = should_trace(source) 86 | 87 | if not trace_stmt: 88 | return 89 | 90 | loader = BirdsEyeLoader(spec, source, deep) 91 | return spec_from_loader(fullname, loader) 92 | 93 | 94 | def should_trace(source): 95 | trace_stmt = None 96 | deep = False 97 | for stmt in ast.parse(source).body: 98 | if isinstance(stmt, ast.Import): 99 | for alias in stmt.names: 100 | if alias.name.startswith('birdseye.trace_module'): 101 | trace_stmt = stmt 102 | if alias.name.endswith('deep'): 103 | deep = True 104 | 105 | if isinstance(stmt, ast.ImportFrom) and stmt.module == 'birdseye': 106 | for alias in stmt.names: 107 | if alias.name.startswith('trace_module'): 108 | trace_stmt = stmt 109 | if alias.name.endswith('deep'): 110 | deep = True 111 | return deep, trace_stmt 112 | -------------------------------------------------------------------------------- /tests/samples/ipython.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import socket 3 | import sys 4 | from io import BytesIO, StringIO 5 | from threading import current_thread, Thread 6 | from uuid import uuid4 7 | 8 | from IPython.core.display import HTML, display 9 | from IPython.core.magic import Magics, cell_magic, magics_class 10 | from jinja2 import Environment, PackageLoader, select_autoescape 11 | from traitlets import Unicode, Int, Bool 12 | from werkzeug.local import LocalProxy 13 | from werkzeug.serving import ThreadingMixIn 14 | 15 | from birdseye.bird import PY2, Database 16 | from birdseye import server, eye 17 | 18 | fake_stream = BytesIO if PY2 else StringIO 19 | 20 | thread_proxies = {} 21 | 22 | 23 | def stream_proxy(original): 24 | def p(): 25 | frame = inspect.currentframe() 26 | while frame: 27 | if frame.f_code == ThreadingMixIn.process_request_thread.__code__: 28 | return fake_stream() 29 | frame = frame.f_back 30 | return thread_proxies.get(current_thread().ident, 31 | original) 32 | 33 | return LocalProxy(p) 34 | 35 | 36 | sys.stderr = stream_proxy(sys.stderr) 37 | sys.stdout = stream_proxy(sys.stdout) 38 | 39 | 40 | def run_server(port, bind_host, show_server_output): 41 | if not show_server_output: 42 | thread_proxies[current_thread().ident] = fake_stream() 43 | try: 44 | server.app.run( 45 | debug=True, 46 | port=port, 47 | host=bind_host, 48 | use_reloader=False, 49 | ) 50 | except socket.error: 51 | pass 52 | 53 | 54 | templates_env = Environment( 55 | loader=PackageLoader('birdseye', 'templates'), 56 | autoescape=select_autoescape(['html', 'xml']) 57 | ) 58 | 59 | 60 | @magics_class 61 | class BirdsEyeMagics(Magics): 62 | server_url = Unicode( 63 | u'', config=True, 64 | help='If set, a server will not be automatically started by %%eye. ' 65 | 'The iframe containing birdseye output will use this value as the base ' 66 | 'of its URL.' 67 | ) 68 | 69 | port = Int( 70 | 7777, config=True, 71 | help='Port number for the server started by %%eye.' 72 | ) 73 | 74 | bind_host = Unicode( 75 | '127.0.0.1', config=True, 76 | help='Host that the server started by %%eye listens on. ' 77 | 'Set to 0.0.0.0 to make it accessible anywhere.' 78 | ) 79 | 80 | show_server_output = Bool( 81 | False, config=True, 82 | help='Set to True to show stdout and stderr from the server started by %%eye.' 83 | ) 84 | 85 | db_url = Unicode( 86 | u'', config=True, 87 | help='The database URL that the server started by %%eye reads from. ' 88 | 'Equivalent to the environment variable BIRDSEYE_DB.' 89 | ) 90 | 91 | @cell_magic 92 | def eye(self, _line, cell): 93 | if not self.server_url: 94 | server.db = Database(self.db_url) 95 | server.Function = server.db.Function 96 | server.Call = server.db.Call 97 | server.Session = server.db.Session 98 | Thread( 99 | target=run_server, 100 | args=( 101 | self.port, 102 | self.bind_host, 103 | self.show_server_output, 104 | ), 105 | ).start() 106 | 107 | eye.db = Database(self.db_url) 108 | 109 | def callback(call_id): 110 | """ 111 | Always executes after the cell, whether or not an exception is raised 112 | in the user code. 113 | """ 114 | if call_id is None: # probably means a bug 115 | return 116 | 117 | html = HTML(templates_env.get_template('ipython_iframe.html').render( 118 | call_id=call_id, 119 | url=self.server_url.rstrip('/'), 120 | port=self.port, 121 | container_id=uuid4().hex, 122 | )) 123 | 124 | # noinspection PyTypeChecker 125 | display(html) 126 | 127 | value = eye.exec_ipython_cell(cell, callback) 128 | # Display the value as would happen if the %eye magic wasn't there 129 | return value 130 | -------------------------------------------------------------------------------- /tests/deadcode.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import copy 3 | import dis 4 | import sys 5 | 6 | 7 | sentinel_rep = 2 8 | # generate sentinel at runtime to keep it out of the bytecode 9 | # this allows the algorithm to check also this file 10 | sentinel = "xsglegahghegflgfaih" * sentinel_rep 11 | 12 | 13 | def constant(value): 14 | if sys.version_info >= (3, 6): 15 | return ast.Constant(value=value) 16 | elif isinstance(value, int): 17 | return ast.Num(value) 18 | elif isinstance(value, str): 19 | return ast.Str(value) 20 | else: 21 | raise TypeError 22 | 23 | 24 | def index(value): 25 | if sys.version_info >= (3, 9): 26 | return constant(value) 27 | else: 28 | return ast.Index(constant(value)) 29 | 30 | 31 | class DeadcodeTransformer(ast.NodeTransformer): 32 | def visit(self, node): 33 | constant_type = ( 34 | ast.Constant 35 | if sys.version_info >= (3, 6) 36 | else (ast.Num, ast.Str, ast.Bytes, ast.NameConstant, ast.Ellipsis) 37 | ) 38 | 39 | if getattr(node, "_check_is_deadcode", False): 40 | if isinstance(node, constant_type) and isinstance(node.value, str): 41 | # docstring for example 42 | return constant(sentinel) 43 | 44 | elif isinstance(node, ast.stmt): 45 | return ast.Expr( 46 | value=ast.Call( 47 | func=ast.Name(id="foo", ctx=ast.Load()), 48 | args=[constant(sentinel)], 49 | keywords=[], 50 | ) 51 | ) 52 | elif isinstance(node, ast.expr): 53 | if hasattr(node, "ctx") and isinstance(node.ctx, (ast.Store, ast.Del)): 54 | return ast.Subscript( 55 | value=ast.Name(id="foo", ctx=ast.Load()), 56 | slice=index(sentinel), 57 | ctx=node.ctx, 58 | ) 59 | 60 | else: 61 | 62 | return ast.Subscript( 63 | value=ast.Tuple( 64 | elts=[node, constant(sentinel)], 65 | ctx=ast.Load(), 66 | ), 67 | slice=index(0), 68 | ctx=ast.Load(), 69 | ) 70 | else: 71 | raise TypeError(node) 72 | 73 | else: 74 | return super().visit(node) 75 | 76 | 77 | def is_deadcode(node): 78 | 79 | if isinstance(node, ast.withitem): 80 | node = node.context_expr 81 | 82 | if isinstance(node, ast.ExceptHandler): 83 | node = node.body[0] 84 | 85 | if sys.version_info >= (3,8) and isinstance(node.parent, ast.NamedExpr) and node.parent.target is node: 86 | node = node.parent 87 | 88 | if sys.version_info >= (3,12) and isinstance(node.parent,ast.TypeAlias): 89 | node = node.parent 90 | 91 | if ( 92 | sys.version_info >= (3, 6) 93 | and isinstance(node.parent, ast.AnnAssign) 94 | and node.parent.target is node 95 | ): 96 | # AnnAssign.target has to be ast.Name 97 | node = node.parent 98 | 99 | if hasattr(node, "_is_deadcode"): 100 | return node._is_deadcode 101 | 102 | node._check_is_deadcode = True 103 | 104 | module = node 105 | while hasattr(module, "parent"): 106 | module = module.parent 107 | 108 | assert isinstance(module, ast.Module) 109 | 110 | # create working copy of the ast 111 | module2 = copy.deepcopy(module) 112 | del node._check_is_deadcode 113 | 114 | module2 = ast.fix_missing_locations(DeadcodeTransformer().visit(module2)) 115 | 116 | try: 117 | code = compile(module2, "", "exec") 118 | except: 119 | print(ast.dump(module2)) 120 | raise 121 | 122 | visited = set() 123 | 124 | def contains_sentinel(code): 125 | if code in visited: 126 | return False 127 | 128 | for inst in dis.get_instructions(code): 129 | arg = inst.argval 130 | if isinstance(arg, type(code)) and contains_sentinel(arg): 131 | return True 132 | if arg == sentinel: 133 | return True 134 | 135 | visited.add(code) 136 | return False 137 | 138 | node._is_deadcode = not contains_sentinel(code) 139 | 140 | return node._is_deadcode 141 | -------------------------------------------------------------------------------- /executing/_utils.py: -------------------------------------------------------------------------------- 1 | 2 | import ast 3 | import sys 4 | import dis 5 | from typing import cast, Any,Iterator 6 | import types 7 | 8 | 9 | 10 | def assert_(condition, message=""): 11 | # type: (Any, str) -> None 12 | """ 13 | Like an assert statement, but unaffected by -O 14 | :param condition: value that is expected to be truthy 15 | :type message: Any 16 | """ 17 | if not condition: 18 | raise AssertionError(str(message)) 19 | 20 | 21 | if sys.version_info >= (3, 4): 22 | # noinspection PyUnresolvedReferences 23 | _get_instructions = dis.get_instructions 24 | from dis import Instruction as _Instruction 25 | 26 | class Instruction(_Instruction): 27 | lineno = None # type: int 28 | else: 29 | from collections import namedtuple 30 | 31 | class Instruction(namedtuple('Instruction', 'offset argval opname starts_line')): 32 | lineno = None # type: int 33 | 34 | from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname, findlinestarts, hasname 35 | 36 | # Based on dis.disassemble from 2.7 37 | # Left as similar as possible for easy diff 38 | 39 | def _get_instructions(co): 40 | # type: (types.CodeType) -> Iterator[Instruction] 41 | code = co.co_code 42 | linestarts = dict(findlinestarts(co)) 43 | n = len(code) 44 | i = 0 45 | extended_arg = 0 46 | while i < n: 47 | offset = i 48 | c = code[i] 49 | op = ord(c) 50 | lineno = linestarts.get(i) 51 | argval = None 52 | i = i + 1 53 | if op >= HAVE_ARGUMENT: 54 | oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg 55 | extended_arg = 0 56 | i = i + 2 57 | if op == EXTENDED_ARG: 58 | extended_arg = oparg * 65536 59 | 60 | if op in hasconst: 61 | argval = co.co_consts[oparg] 62 | elif op in hasname: 63 | argval = co.co_names[oparg] 64 | elif opname[op] == 'LOAD_FAST': 65 | argval = co.co_varnames[oparg] 66 | yield Instruction(offset, argval, opname[op], lineno) 67 | 68 | def get_instructions(co): 69 | # type: (types.CodeType) -> Iterator[EnhancedInstruction] 70 | lineno = co.co_firstlineno 71 | for inst in _get_instructions(co): 72 | inst = cast(EnhancedInstruction, inst) 73 | lineno = inst.starts_line or lineno 74 | assert_(lineno) 75 | inst.lineno = lineno 76 | yield inst 77 | 78 | 79 | # Type class used to expand out the definition of AST to include fields added by this library 80 | # It's not actually used for anything other than type checking though! 81 | class EnhancedAST(ast.AST): 82 | parent = None # type: EnhancedAST 83 | 84 | # Type class used to expand out the definition of AST to include fields added by this library 85 | # It's not actually used for anything other than type checking though! 86 | class EnhancedInstruction(Instruction): 87 | _copied = None # type: bool 88 | 89 | 90 | 91 | 92 | 93 | def mangled_name(node): 94 | # type: (EnhancedAST) -> str 95 | """ 96 | 97 | Parameters: 98 | node: the node which should be mangled 99 | name: the name of the node 100 | 101 | Returns: 102 | The mangled name of `node` 103 | """ 104 | 105 | function_class_types=(ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef) 106 | 107 | if isinstance(node, ast.Attribute): 108 | name = node.attr 109 | elif isinstance(node, ast.Name): 110 | name = node.id 111 | elif isinstance(node, (ast.alias)): 112 | name = node.asname or node.name.split(".")[0] 113 | elif isinstance(node, function_class_types): 114 | name = node.name 115 | elif isinstance(node, ast.ExceptHandler): 116 | assert node.name 117 | name = node.name 118 | elif sys.version_info >= (3,12) and isinstance(node,ast.TypeVar): 119 | name=node.name 120 | else: 121 | raise TypeError("no node to mangle") 122 | 123 | if name.startswith("__") and not name.endswith("__"): 124 | 125 | parent,child=node.parent,node 126 | 127 | while not (isinstance(parent,ast.ClassDef) and child not in parent.bases): 128 | if not hasattr(parent,"parent"): 129 | break # pragma: no mutate 130 | 131 | parent,child=parent.parent,parent 132 | else: 133 | class_name=parent.name.lstrip("_") 134 | if class_name!="" and child not in parent.decorator_list: 135 | return "_" + class_name + name 136 | 137 | 138 | 139 | return name 140 | -------------------------------------------------------------------------------- /tests/samples/utils2.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import inspect 3 | import os 4 | import sys 5 | from itertools import chain 6 | 7 | import six 8 | from cheap_repr import cheap_repr, try_register_repr 9 | 10 | NO_ASTTOKENS = ( 11 | 'pypy' in sys.version.lower() 12 | or sys.version_info[:2] in [(3, 4), (3, 8)] 13 | ) 14 | 15 | file_reading_errors = ( 16 | IOError, 17 | OSError, 18 | ValueError # IronPython weirdness. 19 | ) 20 | 21 | 22 | def shitcode(s): 23 | return ''.join( 24 | (c if (0 < ord(c) < 256) else '?') for c in s 25 | ) 26 | 27 | 28 | def truncate(seq, max_length, middle): 29 | if len(seq) > max_length: 30 | left = (max_length - len(middle)) // 2 31 | right = max_length - len(middle) - left 32 | seq = seq[:left] + middle + seq[-right:] 33 | return seq 34 | 35 | 36 | def truncate_string(string, max_length): 37 | return truncate(string, max_length, '...') 38 | 39 | 40 | def truncate_list(lst, max_length): 41 | return truncate(lst, max_length, ['...']) 42 | 43 | 44 | def ensure_tuple(x, split=False): 45 | if split and isinstance(x, six.string_types): 46 | x = x.replace(',', ' ').split() 47 | if not isinstance(x, (list, set, tuple)): 48 | x = (x,) 49 | return tuple(x) 50 | 51 | 52 | def short_filename(code): 53 | result = os.path.basename(code.co_filename) 54 | if result.endswith('.pyc'): 55 | result = result[:-1] 56 | return result 57 | 58 | 59 | def is_comprehension_frame(frame): 60 | return frame.f_code.co_name in ('', '', '') 61 | 62 | 63 | def needs_parentheses(source): 64 | def code(s): 65 | return compile(s.format(source), '', 'eval').co_code 66 | 67 | try: 68 | without_parens = code('{}.x') 69 | except SyntaxError: 70 | # Likely a multiline expression that needs parentheses to be valid 71 | code('({})') 72 | return True 73 | else: 74 | return without_parens != code('({}).x') 75 | 76 | 77 | def with_needed_parentheses(source): 78 | if needs_parentheses(source): 79 | return '({})'.format(source) 80 | else: 81 | return source 82 | 83 | 84 | REPR_TARGET_LENGTH = 100 85 | 86 | 87 | def my_cheap_repr(x): 88 | return cheap_repr(x, target_length=REPR_TARGET_LENGTH) 89 | 90 | 91 | class ArgDefaultDict(dict): 92 | def __init__(self, factory): 93 | super(ArgDefaultDict, self).__init__() 94 | self.factory = factory 95 | 96 | def __missing__(self, key): 97 | result = self[key] = self.factory(key) 98 | return result 99 | 100 | 101 | def optional_numeric_label(i, lst): 102 | if len(lst) == 1: 103 | return '' 104 | else: 105 | return ' ' + str(i + 1) 106 | 107 | 108 | def is_pathlike(x): 109 | if hasattr(os, 'PathLike'): 110 | return isinstance(x, os.PathLike) 111 | 112 | return ( 113 | hasattr(x, '__fspath__') or 114 | # Make a concession for older `pathlib` versions: 115 | (hasattr(x, 'open') and 116 | 'path' in x.__class__.__name__.lower()) 117 | ) 118 | 119 | 120 | try: 121 | iscoroutinefunction = inspect.iscoroutinefunction 122 | except AttributeError: 123 | def iscoroutinefunction(_): 124 | return False 125 | 126 | try: 127 | try_statement = ast.Try 128 | except AttributeError: 129 | try_statement = ast.TryExcept 130 | 131 | 132 | try: 133 | builtins = __import__("__builtin__") 134 | except ImportError: 135 | builtins = __import__("builtins") 136 | 137 | 138 | try: 139 | FormattedValue = ast.FormattedValue 140 | except: 141 | class FormattedValue(object): 142 | pass 143 | 144 | 145 | def no_args_decorator(args, kwargs): 146 | return len(args) == 1 and inspect.isfunction(args[0]) and not kwargs 147 | 148 | 149 | try: 150 | from functools import lru_cache 151 | except ImportError: 152 | from backports.functools_lru_cache import lru_cache 153 | 154 | 155 | class DirectRepr(str): 156 | def __repr__(self): 157 | return self 158 | 159 | 160 | try: 161 | from django.db.models import QuerySet 162 | except ImportError: 163 | class QuerySet(object): 164 | pass 165 | 166 | 167 | def _sample_indices(length, max_length): 168 | if length <= max_length + 2: 169 | return range(length) 170 | else: 171 | return chain(range(max_length // 2), 172 | range(length - max_length // 2, 173 | length)) 174 | 175 | 176 | @try_register_repr('pandas', 'Series') 177 | def _repr_series_one_line(x, helper): 178 | n = len(x) 179 | if n == 0: 180 | return repr(x) 181 | newlevel = helper.level - 1 182 | pieces = [] 183 | maxparts = _repr_series_one_line.maxparts 184 | for i in _sample_indices(n, maxparts): 185 | k = x.index[i:i + 1].format(sparsify=False)[0] 186 | v = x.iloc[i] 187 | pieces.append('%s = %s' % (k, cheap_repr(v, newlevel))) 188 | if n > maxparts + 2: 189 | pieces.insert(maxparts // 2, '...') 190 | return '; '.join(pieces) 191 | -------------------------------------------------------------------------------- /tests/small_samples/46597f8f896f11c5d7f432236344cc7e5645c2a39836eb6abdd2437c0422f0f4.py: -------------------------------------------------------------------------------- 1 | # sample for this mutation 2 | 3 | # +++ b/executing/_position_node_finder.py 4 | # @@ -193,7 +193,7 @@ class PositionNodeFinder(object): 5 | # 6 | # index += 2 7 | # 8 | # - while self.opname(index) in ("CACHE", "EXTENDED_ARG"): 9 | # + while self.opname(index) in ("CACHE", "XXEXTENDED_ARGXX"): 10 | # index += 2 11 | # 12 | # if ( 13 | 14 | 15 | import ast 16 | from abc import ABC, abstractmethod 17 | from collections import defaultdict 18 | from concurrent.futures import Executor, ThreadPoolExecutor, ProcessPoolExecutor 19 | from contextlib import contextmanager 20 | from datetime import datetime 21 | from enum import Enum 22 | from functools import lru_cache, wraps 23 | import itertools 24 | import logging 25 | from multiprocessing import Manager, freeze_support 26 | import os 27 | from pathlib import Path 28 | import pickle 29 | import regex as re 30 | import signal 31 | import sys 32 | import tempfile 33 | import tokenize 34 | import traceback 35 | from typing import Collection, Generator, Generic, Iterable, Pattern, Sequence, Sized, Type, cast, TYPE_CHECKING 36 | from typing_extensions import Final 37 | from mypy_extensions import mypyc_attr 38 | from appdirs import user_cache_dir 39 | from dataclasses import field, replace 40 | import toml 41 | from typed_ast import ast3, ast27 42 | from pathspec import PathSpec 43 | from blib2to3.pytree import Leaf, type_repr 44 | from blib2to3 import pytree 45 | from blib2to3.pgen2 import driver 46 | from blib2to3.pgen2.grammar import Grammar 47 | from blib2to3.pgen2.parse import ParseError 48 | from _black_version import version as __version__ 49 | import colorama 50 | DEFAULT_LINE_LENGTH = 88 51 | DEFAULT_EXCLUDES = '/(\\.direnv|\\.eggs|\\.git|\\.hg|\\.mypy_cache|\\.nox|\\.tox|\\.venv|\\.svn|_build|buck-out|build|dist)/' 52 | DEFAULT_INCLUDES = '\\.pyi?$' 53 | CACHE_DIR = Path 54 | STRING_PREFIX_CHARS: Final = 'furbFURB' 55 | Depth = int 56 | NodeType = int 57 | ParserState = int 58 | LeafID = int 59 | StringID = int 60 | Priority = int 61 | Index = int 62 | LN = Union 63 | Transformer = Callable 64 | Timestamp = float 65 | FileSize = int 66 | CacheInfo = Tuple 67 | Cache = Dict 68 | out = click.secho 69 | err = partial 70 | pygram.initialize 71 | pygram.python_symbols 72 | 73 | class NothingChanged(UserWarning): 74 | pass 75 | 76 | class CannotTransform(Exception): 77 | pass 78 | 79 | class CannotSplit: 80 | pass 81 | 82 | class InvalidInput(ValueError): 83 | pass 84 | E = TypeVar 85 | 86 | def ok() -> T: 87 | pass 88 | 89 | def __init__() -> None: 90 | pass 91 | Result = Union 92 | TMatchResult = TResult 93 | CACHED = 1 94 | PY36_VERSIONS = {TargetVersion.PY36, TargetVersion.PY37, TargetVersion.PY38} 95 | VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {TargetVersion.PY27: {*()}, TargetVersion.PY33: {*()}, TargetVersion.PY34: {*()}, TargetVersion.PY35: {*()}, TargetVersion: {Feature.ASYNC_IDENTIFIERS}, TargetVersion: {*()}, TargetVersion: {Feature.UNICODE_LITERALS, Feature.F_STRINGS, Feature.NUMERIC_UNDERSCORES, Feature.TRAILING_COMMA_IN_CALL, Feature.TRAILING_COMMA_IN_DEF, Feature.ASYNC_KEYWORDS, Feature.ASSIGNMENT_EXPRESSIONS, Feature.POS_ONLY_ARGUMENTS}} 96 | FileMode = Mode 97 | 98 | def supports_feature() -> bool: 99 | pass 100 | 101 | def find_pyproject_toml() -> Optional[str]: 102 | pass 103 | 104 | def parse_pyproject_toml() -> Dict[str, Any]: 105 | pass 106 | 107 | def read_pyproject_toml(ctx: click.Context) -> Optional[str]: 108 | pass 109 | 110 | def target_version_option_callback(p: Union[click.Option, click.Parameter]) -> List[TargetVersion]: 111 | pass 112 | 113 | @click.command(context_settings=dict) 114 | @click.option 115 | @click.Choice 116 | @click.version_option 117 | @click.argument 118 | @click.pass_context 119 | def main() -> None: 120 | pass 121 | 122 | def get_sources() -> Set[Path]: 123 | pass 124 | 125 | def path_empty() -> None: 126 | pass 127 | 128 | def reformat_one() -> None: 129 | pass 130 | 131 | def reformat_many() -> None: 132 | pass 133 | 134 | async def schedule_formatting(loop: asyncio.AbstractEventLoop) -> None: 135 | pass 136 | 137 | def format_file_in_place(src: Path=WriteBack.NO) -> bool: 138 | pass 139 | 140 | def color_diff() -> str: 141 | pass 142 | 143 | def wrap_stream_for_windows() -> Union[io.TextIOWrapper, 'colorama.AnsiToWin32.AnsiToWin32']: 144 | pass 145 | 146 | def format_stdin_to_stdout() -> bool: 147 | pass 148 | 149 | def format_file_contents() -> FileContent: 150 | pass 151 | 152 | def format_str() -> FileContent: 153 | pass 154 | 155 | def decode_bytes(src: bytes) -> Tuple[FileContent, Encoding, NewLine]: 156 | pass 157 | 158 | def get_grammars() -> List[Grammar]: 159 | pass 160 | 161 | def lib2to3_parse() -> Node: 162 | pass 163 | 164 | def lib2to3_unparse() -> str: 165 | pass 166 | 167 | def visit() -> Iterator[T]: 168 | pass 169 | tree_depth: int = 0 170 | WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE} 171 | STATEMENT: Final = {syms.if_stmt, syms.while_stmt, syms.for_stmt, syms.try_stmt, syms.except_clause, syms.with_stmt, syms.funcdef, syms.classdef} 172 | token.tok_name[STANDALONE_COMMENT] = 'STANDALONE_COMMENT' 173 | LOGIC_OPERATORS: Final = {*()} 174 | COMPARATORS: Final = {token.LESS, token.GREATER, token.EQEQUAL, token.NOTEQUAL, token.LESSEQUAL, token.GREATEREQUAL} 175 | MATH_OPERATORS: Final = {token.VBAR, token.CIRCUMFLEX, token.AMPER, token.LEFTSHIFT, token.RIGHTSHIFT, token.PLUS, token.MINUS, token.STAR, token.SLASH, token.DOUBLESLASH, token.PERCENT, token.AT, token.TILDE, token.DOUBLESTAR} 176 | VARARGS_SPECIALS: Final = STARS 177 | VARARGS_PARENTS: Final = {syms.arglist, syms.trailer, syms.typedargslist, syms.varargslist} 178 | UNPACKING_PARENTS: Final = {syms.atom, syms.dictsetmaker, syms.listmaker, syms.testlist_gexp, syms.testlist_star_expr} 179 | {syms.test, syms.lambdef, syms.or_test, syms.and_test, syms.not_test} 180 | 181 | @dataclass 182 | class BracketTracker: 183 | pass 184 | -------------------------------------------------------------------------------- /tests/generate_small_sample.py: -------------------------------------------------------------------------------- 1 | from .test_main import TestFiles 2 | from pathlib import Path 3 | import hashlib 4 | 5 | from pysource_minimize import minimize 6 | import sys 7 | import textwrap 8 | import os 9 | import linecache 10 | from executing import Source 11 | from multiprocessing import get_context 12 | import tempfile 13 | import hashlib 14 | import time 15 | import contextlib 16 | import os 17 | from rich.progress import Progress, track 18 | from rich.syntax import Syntax 19 | from rich.console import Console 20 | import argparse 21 | import ast 22 | 23 | last_samples_dir = Path(__file__).parent / "last_samples" 24 | last_samples_dir.mkdir(exist_ok=True) 25 | 26 | 27 | small_samples = Path(__file__).parent / "small_samples" 28 | 29 | 30 | def source_hash(source_code): 31 | return hashlib.sha256(source_code.encode("utf8")).hexdigest() 32 | 33 | 34 | def big_samples(folder): 35 | yield from last_samples_dir.rglob("*.py") 36 | 37 | hashes = set() 38 | 39 | for p in folder.rglob("*.py"): 40 | try: 41 | content = p.read_text() 42 | except: 43 | continue 44 | 45 | if content.count("\n") > 50000: 46 | # Long files take too much time to check and are most likely generated code or repetitive 47 | continue 48 | 49 | h = source_hash(content) 50 | if h in hashes: 51 | continue 52 | hashes.add(h) 53 | yield p 54 | 55 | 56 | def test_file(filename: Path): 57 | code = filename.read_text() 58 | 59 | # Clear caches to avoid accumulating too much data in memory. 60 | # This is usually not a problem for executing, but this usage scenario is different 61 | linecache.clearcache() 62 | for cache_name in ("__source_cache_with_lines", "__executing_cache"): 63 | if hasattr(Source, cache_name): 64 | delattr(Source, cache_name) 65 | 66 | test = TestFiles() 67 | try: 68 | ast.parse(code) 69 | except (RecursionError,SyntaxError): 70 | return True 71 | 72 | try: 73 | with open(os.devnull, "w") as dev_null: 74 | with contextlib.redirect_stderr(dev_null): 75 | with contextlib.redirect_stdout(dev_null): 76 | test.check_filename(filename, check_names=True) 77 | except: 78 | return False 79 | 80 | return True 81 | 82 | 83 | def map_file(filename: Path): 84 | return test_file(filename), filename 85 | 86 | 87 | def main(): 88 | 89 | parser = argparse.ArgumentParser(prog="generate_small_samples") 90 | parser.add_argument("source_folder", default="tests/samples", nargs="?") 91 | 92 | args = parser.parse_args() 93 | 94 | folder = Path(args.source_folder) 95 | 96 | if not (folder.exists() and folder.is_dir()): 97 | print("source_folder has to be an existing directory") 98 | exit(1) 99 | 100 | console = Console() 101 | 102 | end_time = time.time() + 60 * 60 103 | 104 | console.print() 105 | console.print(f"Check files in tests/last_samples and {folder}:") 106 | console.print() 107 | 108 | with Progress() as progress: 109 | 110 | task_collect = progress.add_task(description="collect files ...", total=None) 111 | 112 | with get_context("spawn").Pool(maxtasksperchild=100) as p: 113 | files = list( 114 | progress.track( 115 | big_samples(folder), 116 | task_id=task_collect, 117 | description="collect files...", 118 | ) 119 | ) 120 | progress.reset(task_collect, description="check files...", total=len(files)) 121 | 122 | for result, filename in progress.track( 123 | p.imap_unordered(map_file, files), task_id=task_collect 124 | ): 125 | 126 | break_file = Path(__file__).parent / "break_generate" 127 | if break_file.exists(): 128 | break_file.unlink() 129 | sys.exit(0) 130 | 131 | 132 | if not result: 133 | print(f"{filename} is failing the tests -> minimize\n") 134 | failing_code = filename.read_text() 135 | break 136 | else: 137 | progress.stop() 138 | console.print() 139 | console.print( 140 | f" :fireworks: checked {len(files)} files and everything was ok :fireworks:" 141 | ) 142 | console.print() 143 | return 144 | 145 | p.terminate() 146 | 147 | (last_samples_dir / f"{source_hash(failing_code)}.py").write_text(failing_code) 148 | 149 | def check_for_error(source: str): 150 | with tempfile.NamedTemporaryFile(delete=False) as tmp_file: 151 | tmp_file.write(source.encode("utf8")) 152 | tmp_file.flush() 153 | test_ok = test_file(Path(tmp_file.name)) 154 | return not test_ok 155 | 156 | task_minimize = progress.add_task("minimize...") 157 | 158 | def update(current, total): 159 | progress.update(task_minimize, completed=total - current, total=total) 160 | 161 | min_code = minimize(failing_code, check_for_error, progress_callback=update) 162 | 163 | name = f"{source_hash(min_code)}.py" 164 | 165 | mutmut_header = os.environ.get("MUTMUT_HEADER", None) 166 | header = "" 167 | if mutmut_header != None: 168 | header = ( 169 | "This sample was generated for the following code mutation detected by mutmut:\n\n" 170 | + mutmut_header 171 | ) 172 | 173 | header = textwrap.indent(header, "# ", lambda _: True) + "\n" 174 | name = f"{source_hash(header)}.py" 175 | 176 | min_code = header + min_code 177 | 178 | result_location = small_samples / name 179 | result_location.write_text(min_code) 180 | 181 | console.print() 182 | console.print("This is the minimal example to reproduce the bug:") 183 | console.print(Syntax.from_path(result_location, line_numbers=True)) 184 | console.print() 185 | 186 | console.print(f"The example was saved under:\n [blue]{result_location}") 187 | console.print() 188 | 189 | console.print("This example is now part of the test and can be run with:") 190 | console.print( 191 | f" > tox -e py{sys.version_info.major}{sys.version_info.minor} -- -k {name[:10]}" 192 | ) 193 | console.print() 194 | 195 | console.print( 196 | "Have fun debugging :smiley: and dont forget to run me again," 197 | " if you think you fixed everything." 198 | ) 199 | 200 | 201 | if __name__ == "__main__": 202 | main() 203 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import ast 3 | import inspect 4 | from collections import namedtuple 5 | 6 | import executing.executing 7 | 8 | from executing.executing import mangled_name, Instruction 9 | try: 10 | from dis import Instruction as DisInstruction 11 | except ImportError: 12 | DisInstruction = None 13 | 14 | executing.executing.TESTING = 1 15 | 16 | from executing import Source 17 | 18 | non_existing_argument=object() 19 | 20 | class Tester(object): 21 | def __init__(self): 22 | self.decorators = [] 23 | self.__name__ = "" # weird pypy3.6 thing 24 | 25 | def test_set_private_attrs(self): 26 | # Test that attributes with leading __ are handled properly, 27 | # as Python mangles their names. 28 | self.a, self.aa, self._a, self.__a, self.__aa = range(5) 29 | 30 | def check_decorators(self, expected): 31 | assert self.decorators == expected, (self.decorators, expected) 32 | self.decorators = [] 33 | 34 | def get_node(self, typ): 35 | ex = self.get_executing(inspect.currentframe().f_back.f_back) 36 | node = ex.node 37 | assert isinstance(node, typ), (node, typ) 38 | return node 39 | 40 | def get_executing(self, frame): 41 | Source.lazycache(frame) 42 | return Source.executing(frame) 43 | 44 | def check(self, node, value): 45 | frame = inspect.currentframe().f_back.f_back 46 | result = eval( 47 | compile(ast.Expression(node), frame.f_code.co_filename, 'eval'), 48 | frame.f_globals, 49 | frame.f_locals, 50 | ) 51 | assert result == value, (result, value) 52 | 53 | def __call__(self, arg=non_existing_argument, check_func=True): 54 | ex = self.get_executing(inspect.currentframe().f_back) 55 | if ex.decorator: 56 | assert {ex.node} == ex.statements 57 | self.decorators.append(ex.node.decorator_list.index(ex.decorator)) 58 | else: 59 | call = ex.node 60 | if arg is non_existing_argument: 61 | assert len(call.args)==0 62 | else: 63 | self.check(call.args[0], arg) 64 | 65 | if check_func: 66 | self.check(call.func, self) 67 | if ( 68 | isinstance(call.parent, (ast.ClassDef, ast.FunctionDef)) 69 | and call in call.parent.decorator_list 70 | ): 71 | return self 72 | 73 | if arg is non_existing_argument: 74 | return tester 75 | else: 76 | return arg 77 | 78 | def __getattr__(self, item): 79 | parent_frame=inspect.currentframe().f_back 80 | 81 | # pytest is accessing tester to check if it is a test function 82 | if "_pytest" not in parent_frame.f_code.co_filename: 83 | node = self.get_node(ast.Attribute) 84 | self.check(node.value, self) 85 | assert node.attr == item 86 | 87 | return self 88 | 89 | def __getitem__(self, item): 90 | node = self.get_node(ast.Subscript) 91 | self.check(node.value, self) 92 | self.check(subscript_item(node), item) 93 | return self 94 | 95 | def __setattr__(self, name, value): 96 | if name in ('decorators', '__name__'): 97 | super(Tester, self).__setattr__(name, value) 98 | return 99 | 100 | node = self.get_node(ast.Attribute) 101 | self.check(node.value, self) 102 | if node.attr.startswith('__'): 103 | # Account for Python's name mangling of private attributes. 104 | assert name == "_{self.__class__.__name__}{node.attr}".format(self=self, node=node) 105 | else: 106 | assert name == node.attr 107 | assert mangled_name(node) == name 108 | return self 109 | 110 | def __delattr__(self, name): 111 | node = self.get_node(ast.Attribute) 112 | assert isinstance(node.ctx, ast.Del) 113 | assert node.attr == name 114 | 115 | def __setitem__(self, key, value): 116 | node = self.get_node(ast.Subscript) 117 | self.check(node.value, self) 118 | if not isinstance(key, slice): 119 | self.check(subscript_item(node), key) 120 | return self 121 | 122 | def __add__(self, other): 123 | node = self.get_node(ast.BinOp) 124 | self.check(node.left, self) 125 | self.check(node.right, other) 126 | return self 127 | 128 | __pow__ = __mul__ = __sub__ = __add__ 129 | 130 | def __invert__(self): 131 | node = self.get_node(ast.UnaryOp) 132 | self.check(node.operand, self) 133 | return self 134 | 135 | __neg__ = __pos__ = __invert__ 136 | 137 | def __lt__(self, other): 138 | node = self.get_node(ast.Compare) 139 | self.check(node.left, self) 140 | self.check(node.comparators[0], other) 141 | return self 142 | 143 | __ne__ = __ge__ = __lt__ 144 | 145 | def __bool__(self): 146 | if sys.version_info >= (3, 11): 147 | self.get_node(ast.BoolOp) 148 | return False 149 | else: 150 | try: 151 | self.get_node(None) 152 | except RuntimeError: 153 | return False 154 | assert 0 155 | 156 | def __enter__(self): 157 | self.get_node(ast.With) 158 | return self 159 | 160 | def __exit__(self, exc_typ, exc_value, exc_traceback): 161 | self.get_node(ast.With) 162 | 163 | __nonzero__ = __bool__ 164 | 165 | 166 | tester = Tester() 167 | 168 | 169 | def subscript_item(node): 170 | if sys.version_info < (3, 9): 171 | return node.slice.value 172 | else: 173 | return node.slice 174 | 175 | 176 | def in_finally(node): 177 | while hasattr(node, 'parent'): 178 | if isinstance(node.parent, ast.Try) and node in node.parent.finalbody: 179 | return True 180 | node = node.parent 181 | return False 182 | 183 | 184 | SourcePosition = namedtuple("SourcePosition", ["lineno", "col_offset"]) 185 | 186 | 187 | def start_position(obj): 188 | """ 189 | returns the start source position as a (lineno,col_offset) tuple. 190 | obj can be ast.AST or Instruction. 191 | """ 192 | if isinstance(obj, Instruction) or (DisInstruction is not None and isinstance(obj, DisInstruction)): 193 | obj = obj.positions 194 | 195 | if isinstance(obj,ast.Module): 196 | obj=obj.body[0] 197 | 198 | return SourcePosition(obj.lineno, obj.col_offset) 199 | 200 | 201 | def end_position(obj): 202 | """ 203 | returns the end source position as a (lineno,col_offset) tuple. 204 | obj can be ast.AST or Instruction. 205 | """ 206 | if sys.version_info < (3, 8): 207 | return start_position(obj) 208 | 209 | if isinstance(obj, Instruction) or (DisInstruction is not None and isinstance(obj, DisInstruction)): 210 | obj = obj.positions 211 | 212 | if isinstance(obj,ast.Module): 213 | obj=obj.body[-1] 214 | 215 | return SourcePosition(obj.end_lineno, obj.end_col_offset) 216 | -------------------------------------------------------------------------------- /tests/samples/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import ast 4 | import json 5 | 6 | from future import standard_library 7 | 8 | standard_library.install_aliases() 9 | import token 10 | 11 | from future.utils import raise_from 12 | import ntpath 13 | import os 14 | import types 15 | from sys import version_info 16 | from typing import TypeVar, Union, List, Any, Iterator, Tuple, Iterable 17 | 18 | try: 19 | from typing import Type 20 | except ImportError: 21 | Type = type 22 | 23 | try: 24 | from typing import Deque 25 | except ImportError: 26 | from collections import deque as Deque 27 | 28 | try: 29 | from functools import lru_cache 30 | except ImportError: 31 | from backports.functools_lru_cache import lru_cache 32 | 33 | from littleutils import strip_required_prefix 34 | 35 | PY2 = version_info.major == 2 36 | PY3 = not PY2 37 | T = TypeVar('T') 38 | RT = TypeVar('RT') 39 | IPYTHON_FILE_PATH = 'IPython notebook or shell' 40 | FILE_SENTINEL_NAME = '$$__FILE__$$' 41 | 42 | if PY2: 43 | Text = unicode 44 | else: 45 | Text = str 46 | 47 | 48 | def path_leaf(path): 49 | # type: (str) -> str 50 | # http://stackoverflow.com/a/8384788/2482744 51 | head, tail = ntpath.split(path) 52 | return tail or ntpath.basename(head) 53 | 54 | 55 | def common_ancestor(paths): 56 | # type: (List[str]) -> str 57 | """ 58 | Returns a path to a directory that contains all the given absolute paths 59 | """ 60 | prefix = os.path.commonprefix(paths) 61 | 62 | # Ensure that the prefix doesn't end in part of the name of a file/directory 63 | prefix = ntpath.split(prefix)[0] 64 | 65 | # Ensure that it ends with a slash 66 | first_char_after = paths[0][len(prefix)] 67 | if first_char_after in r'\/': 68 | prefix += first_char_after 69 | 70 | return prefix 71 | 72 | 73 | def short_path(path, all_paths): 74 | # type: (str, List[str]) -> str 75 | if path == IPYTHON_FILE_PATH: 76 | return path 77 | 78 | all_paths = [f for f in all_paths 79 | if f != IPYTHON_FILE_PATH] 80 | prefix = common_ancestor(all_paths) 81 | if prefix in r'\/': 82 | prefix = '' 83 | return strip_required_prefix(path, prefix) or path_leaf(path) 84 | 85 | 86 | def fix_abs_path(path): 87 | if path == IPYTHON_FILE_PATH: 88 | return path 89 | if os.path.sep == '/' and not path.startswith('/'): 90 | path = '/' + path 91 | return path 92 | 93 | 94 | if PY2: 95 | def correct_type(obj): 96 | """ 97 | Returns the correct type of obj, regardless of __class__ assignment 98 | or old-style classes: 99 | 100 | >>> class A: 101 | ... pass 102 | ... 103 | ... 104 | ... class B(object): 105 | ... pass 106 | ... 107 | ... 108 | ... class C(object): 109 | ... __class__ = A 110 | ... 111 | >>> correct_type(A()) is A 112 | True 113 | >>> correct_type(B()) is B 114 | True 115 | >>> correct_type(C()) is C 116 | True 117 | """ 118 | t = type(obj) 119 | # noinspection PyUnresolvedReferences 120 | if t is types.InstanceType: 121 | return obj.__class__ 122 | return t 123 | else: 124 | correct_type = type 125 | 126 | 127 | def of_type(type_or_tuple, iterable): 128 | # type: (Union[type, Tuple[Union[type, tuple], ...]], Iterable[Any]) -> Iterator[Any] 129 | return (x for x in iterable if isinstance(x, type_or_tuple)) 130 | 131 | 132 | def safe_next(it): 133 | # type: (Iterator[T]) -> T 134 | """ 135 | next() can raise a StopIteration which can cause strange bugs inside generators. 136 | """ 137 | try: 138 | return next(it) 139 | except StopIteration as e: 140 | raise_from(RuntimeError, e) 141 | raise # isn't reached 142 | 143 | 144 | def one_or_none(expression): 145 | """Performs a one_or_none on a sqlalchemy expression.""" 146 | if hasattr(expression, 'one_or_none'): 147 | return expression.one_or_none() 148 | result = expression.all() 149 | if len(result) == 0: 150 | return None 151 | elif len(result) == 1: 152 | return result[0] 153 | else: 154 | raise Exception("There is more than one item returned for the supplied filter") 155 | 156 | 157 | def flatten_list(lst): 158 | result = [] 159 | for x in lst: 160 | if isinstance(x, list): 161 | result.extend(flatten_list(x)) 162 | else: 163 | result.append(x) 164 | return result 165 | 166 | 167 | def is_lambda(f): 168 | try: 169 | code = f.__code__ 170 | except AttributeError: 171 | return False 172 | return code.co_name == (lambda: 0).__code__.co_name 173 | 174 | 175 | class ProtocolEncoder(json.JSONEncoder): 176 | def default(self, o): 177 | try: 178 | method = o.as_json 179 | except AttributeError: 180 | return super(ProtocolEncoder, self).default(o) 181 | else: 182 | return method() 183 | 184 | 185 | try: 186 | 187 | # Python 3 188 | from tokenize import open as open_with_encoding_check 189 | 190 | except ImportError: 191 | 192 | # Python 2 193 | from lib2to3.pgen2.tokenize import detect_encoding 194 | import io 195 | 196 | 197 | def open_with_encoding_check(filename): # type: ignore 198 | """Open a file in read only mode using the encoding detected by 199 | detect_encoding(). 200 | """ 201 | fp = io.open(filename, 'rb') 202 | try: 203 | encoding, lines = detect_encoding(fp.readline) 204 | fp.seek(0) 205 | text = io.TextIOWrapper(fp, encoding, line_buffering=True) 206 | text.mode = 'r' 207 | return text 208 | except: 209 | fp.close() 210 | raise 211 | 212 | 213 | def read_source_file(filename): 214 | from lib2to3.pgen2.tokenize import cookie_re 215 | 216 | if filename.endswith('.pyc'): 217 | filename = filename[:-1] 218 | 219 | with open_with_encoding_check(filename) as f: 220 | return ''.join([ 221 | '\n' if i < 2 and cookie_re.match(line) 222 | else line 223 | for i, line in enumerate(f) 224 | ]) 225 | 226 | 227 | def source_without_decorators(tokens, function_node): 228 | def_token = safe_next(t for t in tokens.get_tokens(function_node) 229 | if t.string == 'def' and t.type == token.NAME) 230 | 231 | startpos = def_token.startpos 232 | source = tokens.text[startpos:function_node.last_token.endpos].rstrip() 233 | assert source.startswith('def') 234 | 235 | return startpos, source 236 | 237 | 238 | def prn(*args): 239 | for arg in args: 240 | print(arg) 241 | if len(args) == 1: 242 | return args[0] 243 | return args 244 | 245 | 246 | def is_ipython_cell(filename): 247 | return filename.startswith('' % ( 112 | ('ok', 'green') if self.success else 113 | ('remove', 'red'))) 114 | 115 | @property 116 | def success(self): 117 | if self.exception: 118 | assert self.traceback 119 | assert self.return_value == 'None' 120 | return False 121 | else: 122 | assert not self.traceback 123 | return True 124 | 125 | @property 126 | def result(self): 127 | if self.success: 128 | return str(self.return_value) 129 | else: 130 | return str(self.exception) 131 | 132 | @property 133 | def arguments_list(self): 134 | return json.loads(self.arguments) 135 | 136 | @property 137 | def parsed_data(self): 138 | return json.loads(self.data) 139 | 140 | @staticmethod 141 | def basic_dict(call): 142 | return dict(arguments=call.arguments_list, 143 | **select_attrs(call, 'id function_id return_value traceback ' 144 | 'exception start_time')) 145 | 146 | basic_columns = (id, function_id, return_value, 147 | traceback, exception, start_time, arguments) 148 | 149 | class Function(Base): 150 | id = Column(Integer, Sequence('function_id_seq'), primary_key=True) 151 | file = Column(Text) 152 | name = Column(Text) 153 | type = Column(Text) # function or module 154 | html_body = Column(LongText) 155 | lineno = Column(Integer) 156 | data = Column(LongText) 157 | hash = Column(String(length=64), index=True) 158 | body_hash = Column(String(length=64), index=True) 159 | 160 | __table_args__ = ( 161 | UniqueConstraint('hash', 162 | name='everything_unique'), 163 | Index('idx_file', 'file', mysql_length=256), 164 | Index('idx_name', 'name', mysql_length=32), 165 | ) 166 | 167 | @property 168 | def parsed_data(self): 169 | return json.loads(self.data) 170 | 171 | @staticmethod 172 | def basic_dict(func): 173 | return select_attrs(func, 'file name lineno hash body_hash type') 174 | 175 | basic_columns = (file, name, lineno, hash, body_hash, type) 176 | 177 | self.Call = Call 178 | self.Function = Function 179 | self._KeyValue = KeyValue 180 | 181 | self.key_value_store = kv = KeyValueStore() 182 | 183 | if _skip_version_check: 184 | return 185 | 186 | if not self.table_exists(Function): 187 | Base.metadata.create_all(engine) 188 | kv.version = DB_VERSION 189 | elif not self.table_exists(KeyValue) or int(kv.version) < DB_VERSION: 190 | sys.exit('The birdseye database schema is out of date. ' 191 | 'Run "python -m birdseye.clear_db" to delete the existing tables.') 192 | 193 | def table_exists(self, table): 194 | return self.engine.dialect.has_table(self.engine, table.__name__) 195 | 196 | def all_file_paths(self): 197 | # type: () -> List[str] 198 | with self.session_scope() as session: 199 | paths = [f[0] for f in session.query(self.Function.file).distinct() 200 | if not is_ipython_cell(f[0])] 201 | paths.sort() 202 | if IPYTHON_FILE_PATH in paths: 203 | paths.remove(IPYTHON_FILE_PATH) 204 | paths.insert(0, IPYTHON_FILE_PATH) 205 | return paths 206 | 207 | def clear(self): 208 | for model in [self.Call, self.Function, self._KeyValue]: 209 | if self.table_exists(model): 210 | model.__table__.drop(self.engine) 211 | 212 | @contextmanager 213 | def session_scope(self): 214 | """Provide a transactional scope around a series of operations.""" 215 | session = self.Session() 216 | try: 217 | yield session 218 | session.commit() 219 | except: 220 | session.rollback() 221 | raise 222 | finally: 223 | session.close() 224 | 225 | def provide_session(self, func): 226 | @functools.wraps(func) 227 | def wrapper(*args, **kwargs): 228 | with self.session_scope() as session: 229 | return func(session, *args, **kwargs) 230 | 231 | return retry_db(wrapper) 232 | 233 | 234 | # Based on https://docs.sqlalchemy.org/en/latest/errors.html#error-dbapi 235 | retry_db = retry(3, (InterfaceError, OperationalError, InternalError, ProgrammingError)) 236 | -------------------------------------------------------------------------------- /tests/samples/server.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | 3 | import json 4 | from collections import OrderedDict 5 | from functools import partial 6 | from os.path import basename 7 | 8 | from future import standard_library 9 | from littleutils import DecentJSONEncoder, withattrs, group_by_attr 10 | 11 | standard_library.install_aliases() 12 | 13 | import argparse 14 | import os 15 | import sys 16 | 17 | from flask import Flask, request, jsonify, url_for 18 | from flask.templating import render_template 19 | from flask_humanize import Humanize 20 | from werkzeug.routing import PathConverter 21 | import sqlalchemy 22 | 23 | from birdseye.db import Database 24 | from birdseye.utils import short_path, IPYTHON_FILE_PATH, fix_abs_path, is_ipython_cell 25 | 26 | 27 | app = Flask('birdseye') 28 | app.jinja_env.auto_reload = True 29 | 30 | Humanize(app) 31 | 32 | 33 | class FileConverter(PathConverter): 34 | regex = '.*?' 35 | 36 | 37 | app.url_map.converters['file'] = FileConverter 38 | 39 | 40 | db = Database() 41 | Session = db.Session 42 | Function = db.Function 43 | Call = db.Call 44 | 45 | 46 | @app.route('/') 47 | @db.provide_session 48 | def index(session): 49 | all_paths = db.all_file_paths() 50 | 51 | recent_calls = (session.query(*(Call.basic_columns + Function.basic_columns)) 52 | .join(Function) 53 | .order_by(Call.start_time.desc())[:100]) 54 | 55 | files = OrderedDict() 56 | 57 | for row in recent_calls: 58 | if is_ipython_cell(row.file): 59 | continue 60 | files.setdefault( 61 | row.file, OrderedDict() 62 | ).setdefault( 63 | row.name, row 64 | ) 65 | 66 | for path in all_paths: 67 | files.setdefault( 68 | path, OrderedDict() 69 | ) 70 | 71 | short = partial(short_path, all_paths=all_paths) 72 | 73 | return render_template('index.html', 74 | short=short, 75 | files=files) 76 | 77 | 78 | @app.route('/file/') 79 | @db.provide_session 80 | def file_view(session, path): 81 | path = fix_abs_path(path) 82 | 83 | # Get all calls and functions in this file 84 | filtered_calls = (session.query(*(Call.basic_columns + Function.basic_columns)) 85 | .join(Function) 86 | .filter_by(file=path) 87 | .subquery('filtered_calls')) 88 | 89 | # Get the latest call *time* for each function in the file 90 | latest_calls = session.query( 91 | filtered_calls.c.name, 92 | sqlalchemy.func.max(filtered_calls.c.start_time).label('maxtime') 93 | ).group_by( 94 | filtered_calls.c.name, 95 | ).subquery('latest_calls') 96 | 97 | # Get the latest call for each function 98 | query = session.query(filtered_calls).join( 99 | latest_calls, 100 | sqlalchemy.and_( 101 | filtered_calls.c.name == latest_calls.c.name, 102 | filtered_calls.c.start_time == latest_calls.c.maxtime, 103 | ) 104 | ).order_by(filtered_calls.c.start_time.desc()) 105 | funcs = group_by_attr(query, 'type') 106 | 107 | # Add any functions which were never called 108 | all_funcs = sorted(session.query(Function.name, Function.type) 109 | .filter_by(file=path) 110 | .distinct()) 111 | func_names = {row.name for row in query} 112 | for func in all_funcs: 113 | if func.name not in func_names: 114 | funcs[func.type].append(func) 115 | 116 | return render_template('file.html', 117 | funcs=funcs, 118 | is_ipython=path == IPYTHON_FILE_PATH, 119 | full_path=path, 120 | short_path=basename(path)) 121 | 122 | 123 | @app.route('/file//__function__/') 124 | @db.provide_session 125 | def func_view(session, path, func_name): 126 | path = fix_abs_path(path) 127 | query = get_calls(session, path, func_name, 200) 128 | if query: 129 | func = query[0] 130 | calls = [withattrs(Call(), **row._asdict()) for row in query] 131 | else: 132 | func = session.query(Function).filter_by(file=path, name=func_name)[0] 133 | calls = None 134 | 135 | return render_template('function.html', 136 | func=func, 137 | short_path=basename(path), 138 | calls=calls) 139 | 140 | 141 | @app.route('/api/file//__function__//latest_call/') 142 | @db.provide_session 143 | def latest_call(session, path, func_name): 144 | path = fix_abs_path(path) 145 | call = get_calls(session, path, func_name, 1)[0] 146 | return jsonify(dict( 147 | id=call.id, 148 | url=url_for(call_view.__name__, 149 | call_id=call.id), 150 | )) 151 | 152 | 153 | def get_calls(session, path, func_name, limit): 154 | return (session.query(*(Call.basic_columns + Function.basic_columns)) 155 | .join(Function) 156 | .filter_by(file=path, name=func_name) 157 | .order_by(Call.start_time.desc())[:limit]) 158 | 159 | 160 | @db.provide_session 161 | def base_call_view(session, call_id, template): 162 | call = session.query(Call).filter_by(id=call_id).one() 163 | func = call.function 164 | return render_template(template, 165 | short_path=basename(func.file), 166 | call=call, 167 | func=func) 168 | 169 | 170 | @app.route('/call/') 171 | def call_view(call_id): 172 | return base_call_view(call_id, 'call.html') 173 | 174 | 175 | @app.route('/ipython_call/') 176 | def ipython_call_view(call_id): 177 | return base_call_view(call_id, 'ipython_call.html') 178 | 179 | 180 | @app.route('/ipython_iframe/') 181 | def ipython_iframe_view(call_id): 182 | """ 183 | This view isn't generally used, it's just an easy way to play with the template 184 | without a notebook. 185 | """ 186 | return render_template('ipython_iframe.html', 187 | container_id='1234', 188 | port=7777, 189 | call_id=call_id) 190 | 191 | 192 | @app.route('/kill', methods=['POST']) 193 | def kill(): 194 | func = request.environ.get('werkzeug.server.shutdown') 195 | if func is None: 196 | raise RuntimeError('Not running with the Werkzeug Server') 197 | func() 198 | return 'Server shutting down...' 199 | 200 | 201 | @app.route('/api/call/') 202 | @db.provide_session 203 | def api_call_view(session, call_id): 204 | call = session.query(Call).filter_by(id=call_id).one() 205 | func = call.function 206 | return DecentJSONEncoder().encode(dict( 207 | call=dict(data=call.parsed_data, **Call.basic_dict(call)), 208 | function=dict(data=func.parsed_data, **Function.basic_dict(func)))) 209 | 210 | 211 | @app.route('/api/calls_by_body_hash/') 212 | @db.provide_session 213 | def calls_by_body_hash(session, body_hash): 214 | query = (session.query(*Call.basic_columns + (Function.data,)) 215 | .join(Function) 216 | .filter_by(body_hash=body_hash) 217 | .order_by(Call.start_time.desc())[:200]) 218 | 219 | calls = [Call.basic_dict(withattrs(Call(), **row._asdict())) 220 | for row in query] 221 | 222 | function_data_set = {row.data for row in query} 223 | ranges = set() 224 | loop_ranges = set() 225 | for function_data in function_data_set: 226 | function_data = json.loads(function_data) 227 | 228 | def add(key, ranges_set): 229 | for node in function_data[key]: 230 | ranges_set.add((node['start'], node['end'])) 231 | 232 | add('node_ranges', ranges) 233 | 234 | # All functions are expected to have the same set 235 | # of loop nodes 236 | current_loop_ranges = set() 237 | add('loop_ranges', current_loop_ranges) 238 | assert loop_ranges in (set(), current_loop_ranges) 239 | loop_ranges = current_loop_ranges 240 | 241 | ranges = [dict(start=start, end=end) for start, end in ranges] 242 | loop_ranges = [dict(start=start, end=end) for start, end in loop_ranges] 243 | 244 | return DecentJSONEncoder().encode(dict( 245 | calls=calls, ranges=ranges, loop_ranges=loop_ranges)) 246 | 247 | 248 | @app.route('/api/body_hashes_present/', methods=['POST']) 249 | @db.provide_session 250 | def body_hashes_present(session): 251 | hashes = request.get_json() 252 | query = (session.query(Function.body_hash, sqlalchemy.func.count(Call.id)) 253 | .outerjoin(Call) 254 | .filter(Function.body_hash.in_(hashes)) 255 | .group_by(Function.body_hash)) 256 | return DecentJSONEncoder().encode([ 257 | dict(hash=h, count=count) 258 | for h, count in query 259 | ]) 260 | 261 | 262 | def main(argv=sys.argv[1:]): 263 | # Support legacy CLI where there was just one positional argument: the port 264 | if len(argv) == 1 and argv[0].isdigit(): 265 | argv.insert(0, '--port') 266 | 267 | parser = argparse.ArgumentParser(description="Bird's Eye: A graphical Python debugger") 268 | parser.add_argument('-p', '--port', help='HTTP port, default is 7777', default=7777, type=int) 269 | parser.add_argument('--host', help="HTTP host, default is 'localhost'", default='localhost') 270 | 271 | args = parser.parse_args(argv) 272 | app.run( 273 | port=args.port, 274 | host=args.host, 275 | use_reloader=os.environ.get('BIRDSEYE_RELOADER') == '1', 276 | ) 277 | 278 | 279 | if __name__ == '__main__': 280 | main() 281 | -------------------------------------------------------------------------------- /tests/samples/tracer2.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import inspect 3 | import os 4 | import re 5 | import sys 6 | import threading 7 | from collections import OrderedDict 8 | 9 | import six 10 | # noinspection PyUnresolvedReferences 11 | from cheap_repr import cheap_repr, find_repr_function 12 | 13 | from snoop.utils import my_cheap_repr, NO_ASTTOKENS, ArgDefaultDict, iscoroutinefunction, \ 14 | truncate_list, ensure_tuple, is_comprehension_frame, no_args_decorator 15 | from .formatting import Event, Source 16 | from .variables import CommonVariable, Exploding, BaseVariable 17 | 18 | find_repr_function(six.text_type).maxparts = 100 19 | find_repr_function(six.binary_type).maxparts = 100 20 | find_repr_function(object).maxparts = 100 21 | find_repr_function(int).maxparts = 999999 22 | cheap_repr.suppression_threshold = 999999 23 | 24 | 25 | class FrameInfo(object): 26 | def __init__(self, frame): 27 | self.frame = frame 28 | self.local_reprs = {} 29 | self.last_line_no = frame.f_lineno 30 | self.comprehension_variables = OrderedDict() 31 | self.source = Source.for_frame(frame) 32 | self.is_generator = frame.f_code.co_flags & inspect.CO_GENERATOR 33 | self.had_exception = False 34 | if is_comprehension_frame(frame): 35 | self.comprehension_type = ( 36 | re.match(r'<(\w+)comp>', frame.f_code.co_name).group(1).title() 37 | + u' comprehension' 38 | ) 39 | else: 40 | self.comprehension_type = '' 41 | 42 | def update_variables(self, watch, watch_extras, event): 43 | self.last_line_no = self.frame.f_lineno 44 | old_local_reprs = self.local_reprs 45 | self.local_reprs = OrderedDict( 46 | (source, my_cheap_repr(value)) 47 | for source, value in 48 | self.get_local_reprs(watch, watch_extras) 49 | ) 50 | 51 | if self.comprehension_type: 52 | for name, value_repr in self.local_reprs.items(): 53 | values = self.comprehension_variables.setdefault(name, []) 54 | if not values or values[-1] != value_repr: 55 | values.append(value_repr) 56 | values[:] = truncate_list(values, 11) 57 | if event in ('return', 'exception'): 58 | return [ 59 | (name, ', '.join(values)) 60 | for name, values in self.comprehension_variables.items() 61 | ] 62 | else: 63 | return [] 64 | 65 | variables = [] 66 | for name, value_repr in self.local_reprs.items(): 67 | if name not in old_local_reprs or old_local_reprs[name] != value_repr: 68 | variables.append((name, value_repr)) 69 | return variables 70 | 71 | def get_local_reprs(self, watch, watch_extras): 72 | frame = self.frame 73 | code = frame.f_code 74 | vars_order = code.co_varnames + code.co_cellvars + code.co_freevars + tuple(frame.f_locals.keys()) 75 | 76 | result_items = sorted( 77 | frame.f_locals.items(), 78 | key=lambda key_value: vars_order.index(key_value[0]) 79 | ) 80 | 81 | for variable in watch: 82 | result_items += sorted(variable.items(frame)) 83 | 84 | for source, value in result_items: 85 | yield source, value 86 | for extra in watch_extras: 87 | try: 88 | pair = extra(source, value) 89 | except Exception: 90 | pass 91 | else: 92 | if pair is not None: 93 | assert len(pair) == 2, "Watch extra must return pair or None" 94 | yield pair 95 | 96 | 97 | thread_global = threading.local() 98 | internal_directories = (os.path.dirname((lambda: 0).__code__.co_filename),) 99 | 100 | try: 101 | # noinspection PyUnresolvedReferences 102 | import birdseye 103 | except ImportError: 104 | pass 105 | else: 106 | internal_directories += (os.path.dirname(birdseye.__file__),) 107 | 108 | 109 | class TracerMeta(type): 110 | def __new__(mcs, *args, **kwargs): 111 | result = super(TracerMeta, mcs).__new__(mcs, *args, **kwargs) 112 | result.default = result() 113 | return result 114 | 115 | def __call__(cls, *args, **kwargs): 116 | if no_args_decorator(args, kwargs): 117 | return cls.default(args[0]) 118 | else: 119 | return super(TracerMeta, cls).__call__(*args, **kwargs) 120 | 121 | def __enter__(self): 122 | return self.default.__enter__(context=1) 123 | 124 | def __exit__(self, *args): 125 | return self.default.__exit__(*args, context=1) 126 | 127 | 128 | @six.add_metaclass(TracerMeta) 129 | class Tracer(object): 130 | def __init__( 131 | self, 132 | watch=(), 133 | watch_explode=(), 134 | depth=1, 135 | ): 136 | self.watch = [ 137 | v if isinstance(v, BaseVariable) else CommonVariable(v) 138 | for v in ensure_tuple(watch) 139 | ] + [ 140 | v if isinstance(v, BaseVariable) else Exploding(v) 141 | for v in ensure_tuple(watch_explode) 142 | ] 143 | self.frame_infos = ArgDefaultDict(FrameInfo) 144 | self.depth = depth 145 | assert self.depth >= 1 146 | self.target_codes = set() 147 | self.target_frames = set() 148 | 149 | def __call__(self, function): 150 | if iscoroutinefunction(function): 151 | raise NotImplementedError("coroutines are not supported, sorry!") 152 | 153 | self.target_codes.add(function.__code__) 154 | 155 | @functools.wraps(function) 156 | def simple_wrapper(*args, **kwargs): 157 | with self: 158 | return function(*args, **kwargs) 159 | 160 | @functools.wraps(function) 161 | def generator_wrapper(*args, **kwargs): 162 | gen = function(*args, **kwargs) 163 | method, incoming = gen.send, None 164 | while True: 165 | with self: 166 | try: 167 | outgoing = method(incoming) 168 | except StopIteration: 169 | return 170 | try: 171 | method, incoming = gen.send, (yield outgoing) 172 | except Exception as e: 173 | method, incoming = gen.throw, e 174 | 175 | if inspect.isgeneratorfunction(function): 176 | return generator_wrapper 177 | else: 178 | return simple_wrapper 179 | 180 | def __enter__(self, context=0): 181 | if not self.config.enabled: 182 | return 183 | 184 | calling_frame = sys._getframe(context + 1) 185 | if not self._is_internal_frame(calling_frame): 186 | calling_frame.f_trace = self.trace 187 | self.target_frames.add(calling_frame) 188 | self.config.last_frame = calling_frame 189 | self.trace(calling_frame, 'enter', None) 190 | 191 | stack = thread_global.__dict__.setdefault('original_trace_functions', []) 192 | stack.append(sys.gettrace()) 193 | sys.settrace(self.trace) 194 | 195 | def __exit__(self, exc_type, exc_value, exc_traceback, context=0): 196 | if not self.config.enabled: 197 | return 198 | 199 | stack = thread_global.original_trace_functions 200 | sys.settrace(stack.pop()) 201 | calling_frame = sys._getframe(context + 1) 202 | self.trace(calling_frame, 'exit', None) 203 | self.target_frames.discard(calling_frame) 204 | self.frame_infos.pop(calling_frame, None) 205 | 206 | def _is_internal_frame(self, frame): 207 | return frame.f_code.co_filename.startswith(internal_directories) 208 | 209 | def _is_traced_frame(self, frame): 210 | return frame.f_code in self.target_codes or frame in self.target_frames 211 | 212 | def trace(self, frame, event, arg): 213 | if not self._is_traced_frame(frame): 214 | if ( 215 | self.depth == 1 216 | or self._is_internal_frame(frame) 217 | ) and not is_comprehension_frame(frame): 218 | return None 219 | else: 220 | candidate = frame 221 | i = 0 222 | while True: 223 | if is_comprehension_frame(candidate): 224 | candidate = candidate.f_back 225 | continue 226 | i += 1 227 | if self._is_traced_frame(candidate): 228 | break 229 | candidate = candidate.f_back 230 | if i >= self.depth or candidate is None or self._is_internal_frame(candidate): 231 | return None 232 | 233 | thread_local = self.config.thread_local 234 | thread_local.__dict__.setdefault('depth', -1) 235 | frame_info = self.frame_infos[frame] 236 | if event in ('call', 'enter'): 237 | thread_local.depth += 1 238 | elif self.config.last_frame and self.config.last_frame is not frame: 239 | line_no = frame_info.last_line_no 240 | trace_event = Event(frame_info, event, arg, thread_local.depth, line_no=line_no) 241 | line = self.config.formatter.format_line_only(trace_event) 242 | self.config.write(line) 243 | 244 | if event == 'exception': 245 | frame_info.had_exception = True 246 | 247 | self.config.last_frame = frame 248 | 249 | trace_event = Event(frame_info, event, arg, thread_local.depth) 250 | if not (frame.f_code.co_name == '' and event not in ('return', 'exception')): 251 | trace_event.variables = frame_info.update_variables( 252 | self.watch, 253 | self.config.watch_extras, 254 | event, 255 | ) 256 | 257 | if event in ('return', 'exit'): 258 | del self.frame_infos[frame] 259 | thread_local.depth -= 1 260 | 261 | formatted = self.config.formatter.format(trace_event) 262 | self.config.write(formatted) 263 | 264 | return self.trace 265 | 266 | 267 | class Spy(object): 268 | def __init__(self, config): 269 | self.config = config 270 | 271 | def __call__(self, *args, **kwargs): 272 | if NO_ASTTOKENS: 273 | raise Exception("birdseye doesn't support this version of Python") 274 | 275 | try: 276 | import birdseye 277 | except ImportError: 278 | raise Exception("You must install birdseye separately to use spy: pip install birdseye") 279 | 280 | # Decorator without parentheses 281 | if no_args_decorator(args, kwargs): 282 | return self._trace(args[0]) 283 | 284 | # Decorator with parentheses and perhaps arguments 285 | def decorator(func): 286 | return self._trace(func, *args, **kwargs) 287 | 288 | return decorator 289 | 290 | def _trace(self, func, *args, **kwargs): 291 | # noinspection PyUnresolvedReferences 292 | from birdseye import eye 293 | 294 | traced = eye(func) 295 | traced = self.config.snoop(*args, **kwargs)(traced) 296 | 297 | @functools.wraps(func) 298 | def wrapper(*func_args, **func_kwargs): 299 | if self.config.enabled: 300 | final_func = traced 301 | else: 302 | final_func = func 303 | 304 | return final_func(*func_args, **func_kwargs) 305 | 306 | return wrapper 307 | --------------------------------------------------------------------------------