├── tests ├── __init__.py ├── valid_source_samples │ ├── e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.py │ ├── e49b5c7a9e738d485bc77b54d9906a928859295bcd805a4600efcb51f1588e9e.py │ ├── efa099f3f0e0f121092ccc80748e056d1950b3f293f48d1dfa16a76b25afe143.py │ ├── 412b0c5f596fee0b7014653e45a3199feac3a4af1ebe3a37b6d37ed0ee02b461.py │ ├── ace08d0effbd437bc88615a8851496dc6eee9e5abb5f9dc3e977ba84f7ab3592.py │ ├── 193baa3771b0bd2bbedb024ebd6251a3c520ea76dbf5fc0250866a15dcbba9a1.py │ ├── 622304cbf3785ec23201b087777987082588575bf1a655cb149b5b8265cb5880.py │ ├── 8201900a0c7ad514550cea8bbf502fc5999e7ded8b5d6188a1d6398a294fed4b.py │ ├── 1cef236c84cad928164392a320d63d433857e8948d5093d54a4f8ef9c98d2093.py │ ├── 5b89856e57891de7a2520769129ce1f62b163a4fc99f17cec0a7b43dc626a9e5.py │ ├── 5c2f9e9ccf5b6b88b118bdcdb0497214f9884f839b82d9a75da2577090f4ec47.py │ ├── a6e3d966873ac026adfc99771d14e0582e4c5464736128a89c6828dcad1f01e9.py │ ├── 24073542e630cba05794fb436ff8e55801c06f33656a2090e816f12dbb66bf29.py │ ├── ad8aa993e6ee4eb5ee764d55f2e3fd636a99b2ecb8c5aff2b35fbb78a074ea30.py │ ├── fcba90c741e667a0c674c9b2ef5c6486e4ebff34bf52bed5780be3cd9164c941.py │ ├── 05bdd936a1c7835b41d94fd1ae8b87c39cea1754aa9bb217d22aeb2d78032472.py │ ├── 15ef8f5d16cd5adb4f571c4e6e261c26a1b9b93f56901e59d21241b3fa9f7e07.py │ ├── 3746199c279c8163acf64ad9bc2f223a79c438c2198988bb5012111e38f44f8d.py │ ├── 360c8863298603afc729c3b81eaaa3bd4b49529bb3f5818c33036fefe960733d.py │ ├── 5f0d8da5e2b7ab1505151a242130539632fb8e4e1d2d3a8b2df3c24e035610f0.py │ ├── 8d9b3e8ebe7bdd8ce31cdb3a8441fe20564dc087ee1901876e7259d533c68282.py │ ├── 7113d3b0d32e5bcc8109106f6bcbfdb82a5ef254706752f1a37084ccae1e5de5.py │ ├── b4f38ff898ca652e599bc5927989f3afe68283f7bbd1eea01624e90b39f53410.py │ ├── e76d44e9af60d7d5d9228f80b96af8efc673ef861bf1683360162ea6551226e8.py │ ├── 2feb13c351ec7534080b009d102f5bf1e6c321eb6fb4253bbb10e236b80fb0ca.py │ ├── 3f7aaaf6a910fcca6ae7e096af1c80769114c0ef6abd1e531bdc0f082b2e8004.py │ ├── e02b69c37d775f00b3682c8ee9094fa145cb1ece8166ad5cbac720888308413c.py │ ├── 82f80c15eaabb0f140d7bc1690a1cef1ac9c600a0e089fdc10b4aa8e8d477fe6.py │ ├── 1d98049a3f80c5a41355f65ad2fdddcffc7d48090740133f832eb24ef825d912.py │ ├── 01ccb7937a32d6d5f0198c24825be34428c24a539d127a2954c3cd837f319120.py │ ├── 4db28b9b8f61b1f776338a1fa25d1b424a3c5c3803acd069d16e233a98dfa28e.py │ ├── bac11acb665178bb107402a47c0eed8e58175552d74f41b3870f6617fbc03c6c.py │ ├── aeba483abfb424f27b4c6bf11671e54d2db676635607f32724e2b1bc34a7b24b.py │ ├── d35eb119be1cd86d5059f9fe44def6770f23e2a774a53c596abe3242a523a718.py │ ├── ec595d73209619a769a17cf5a90de184b7a414b0d9ddd473dd9388df8a77944c.py │ ├── 40b181724590fa49314c1ec128d415b9eebc1b89a756869972303ee344548b16.py │ ├── 15d11c996d51cad39acbf52f2de302edfff89564e9c644fb24a6fbc1d71ad74f.py │ ├── e58893bc48a383fa027aef9d985a26bc2eac09e57a4b4f6b92c4bb940eee5a0e.py │ ├── 4514e678724a6124c3dac5883c4bc0305e080f7b8928f9ce30ad1ca4e29b724c.py │ └── cf3df6db1ef8d4b989e4177d63a93907a3f168fcc0a74ac9aceecf20ca274c1b.py ├── invalid_ast_samples │ ├── d5d29005017d00b30bd6f192bf87b2f6406e6cd5af0c847d6df01607eb9e1017.py │ ├── 3ea606b5c05ede738f7f7c33dc9ad02e0dfe9da2ab12e44c9bea55e10afa4be1.py │ ├── 405ad40048c979d08fac3374dd3305256f4a1918672e9fdf609e50da9ea08224.py │ ├── fb5b7e98a81fac64880a9521f0af8887d739fd1ad60b8d56006eece46cc42305.py │ ├── dae694f70daffbafb3644d1800924e139266d9b86f9d5bd4d0c519a3fe5c49fb.py │ ├── 00d09cb4bde4e8e66bef022dfa684fa3e11b6cdf49e361d591a4799244ab70ca.py │ ├── 8e31e112e57521da11f8c8fc22d6793c7eaba0da4093e272de02fe22de9d83c4.py │ ├── 57b88e20155b552e2db2f16eb52816bbe33d54239511c714d6f554d61176de09.py │ ├── a7d9ae6980afa14378caef3b2590be38e03a322b9106538e92ed896ed9bb926c.py │ ├── 9e0944875f10490b5ac4a9f3d9d545f09d5e58fd18887e44f47e1a0f59ddac12.py │ ├── 53a16f126fca2bf9dd710bf96c71c070ceb18f1fab1111bafddc5ac8b82e576f.py │ ├── cd92d1c9ed898874c4ae2eca3d0789ed5eff628fe184b97fba6e6a0ddf1aa1a8.py │ ├── c4bcf0c9d029faaa9e471d953ae30cb9e75e4a44956a380058c9a523f1b72e75.py │ ├── 2bd68197b059e2fa32971c238286d02e97c73eab57c32db0a594d6af8c4ecb08.py │ ├── 31ca384bae299cab7bf96ad606b3383a0d6df0acd8ead1b7ae1cab38eae6ab12.py │ ├── 56e2e8a599b5dad0b7ba95940875950926e8306360de8b4c3a03605cf02a983c.py │ ├── 9aaae4210d9083b69f97ecaa5823c1cd9a1937a8ae06a26407c3755c25acebc7.py │ ├── aa0faa8978ee0d835e4169c35ea1521e7035a70bf0b0614041da3ede2016fcb8.py │ ├── 4c7eea039f102b91ce5dc6c478a59be47743cc32eb93e4a10b2ed22eb165eab9.py │ ├── 70761b2829f2bc6774b0ad7a8d3127729565ee384f4a78e1e3502a970682fde7.py │ ├── 08c8e43f21d07d8d79668106ab21923fd39fff654bf79f957ec8d1c2d1d33b2a.py │ ├── 811b5754f3a5636dcac40d0e5f6dca5d4b09324875b9217b6f1d366a98a99d02.py │ ├── 7fb058ec8db27618d6902b5374691db79e85bf981ee266f1e56075fcd54ccc75.py │ ├── c2d9be1ca1ecf5bd343ebc53651fe24a32d50eb52bb8950f041fc20169d33460.py │ ├── 73a79ca089e41554f52abac852d999fd1c8b6e0d87243cd1d03ac6fb2a066a8f.py │ ├── 7bc6ba4645ea543c31ce117591dda826529c17d1d5d7aaf10edca103b96cfaa4.py │ ├── 90186e3f0182378d8e86f963dd01fdb315ec8de19d2c9fff086986d7f64f88e4.py │ ├── b342ea89f9e8d4e54ac09e1752bfa6f35d69a0788e7831bd2f5e2f238755d2f2.py │ ├── 1bcbddd3ebb0de15ab35ac4df52a5cbab624d9624ddcf5185223d8067b666e4c.py │ ├── a0e15b43dc1f959d9c2aa44a923d267480b3b03adf2bfe86e460ff8229c3aafd.py │ ├── fcfb014cd6eef719639604562887fc22d165c684bc24fdbb00b6d1be818ce947.py │ ├── 201fd1188d01b64e59912d50e9903e9c0cfd5e39b8716ea859e1e9aa506c5c62.py │ ├── eb58c698896405e41e912e403f8ea4fd66953b64cf5adde3a8988551d71e3324.py │ ├── 85e465ef7787fa3b3c32a4e1c7c49538ca87f1090438fabe9307ac221a0d0a73.py │ ├── 9202b8548441a2959111ed2169f659b7f13fd6f9f165c5fc8f067c733bf06111.py │ ├── c04e4881fbbd2d7a352b4a103a55f894a6c4e7e2299211521c38ea34d1165d26.py │ ├── c9106824d0e85940e71276123bd6bc082058789de30886c12a94e6c680eac9a5.py │ ├── 6d75350e42778cb8e1d1f10eee27a79dc00f6a3e40bacebefc7d490e662a1856.py │ ├── 8fb26fb07a3731362c51e638f159532d803a5aa8d5b55d68332126d6f1f94692.py │ ├── d8fe8ca9a8f439de6b8552a8b39ec22a7efca65bcadcbb9945cc01775b6d5af8.py │ ├── 416736d2215d8e7fc07352649f7419d9569294aebab30b0caefc5d259007a6d6.py │ ├── 49981c344fde47e1eb48aaefb32b9b6d944ba34a3f9db4d667c865af51c7dfcf.py │ ├── bd81263ead51ea209adf81302d2bc2fe4b3562c363e39111a61d9bac8f58d632.py │ ├── 80f13eb1d4cc2a045f57bba482a24090e67040a15dffa12479ec05077eb9e1aa.py │ ├── f74405488bd9786f83afb55c357ab2589c64db9a19984eab3d6c99de2700e511.py │ ├── 340f35d4e8b3e2aed11c02f04a3a9f0e6f600f480e675902b3f041be8b5df0c4.py │ ├── b13adc212d95bd6b366b45ad5d8e5e7525d7edefc8e5f9a9fc62a60be83cd1b4.py │ ├── 42b9134add4c95799f19958d1476a8eaf748e6421332cd3449638f941c809e54.py │ ├── 5af501bb4a4f8582f357e34db766519c545e50f4d20713986b4ce2922dcdadd6.py │ ├── 619370089c11db7565ceda70b818fccfaebdc1f8658068699f54053803aba62f.py │ ├── c56942a0cb4b89e2b08ff2e4c6d93e6f60b2d960aa7b0174c7128d2f06f0e270.py │ ├── d0b5e1927a82c7c2253bd4c27840023544dc9bd15eedd7a804e3e698ff1d6e77.py │ ├── 0e3cc3a70bb8ca79e3e6e21ad45fe7caa36411970ae2167a2d0a17b40c095646.py │ ├── 54c0b446dc337abdc16c50d7928dd3c9cca98b6d8884d30d57aa1001c3142ff8.py │ ├── 567b705d091d5a43efcaf38c9980921e1ee819e1c33484d90308b96d57acb896.py │ ├── 7db01336123c8c30720412ce816e728caa712a3541427f18796d9ee26195e498.py │ ├── 96417e0aac6381dc499c7697dcc092589a773523c1bb796285dd2a8181606826.py │ ├── 72bc8ecc4a0486fb1bc845d6ff447542a77ffdd6c527fa229a3d0879938cac6f.py │ ├── ed250c4b725d132c3f0a6515a1997c52f5fa9e20fca33ce90315de62d8bebe27.py │ ├── e46612db2bd98f7426567240a230c786129e039d96c0f7d75a9484c5728b024b.py │ ├── 634715912e059f073f55ba9d3d48d71a3d23cf0ff4b4bcfe24a442b1cd62475c.py │ ├── 15ae66a5d7d947c199b82be80107b035dcd47da0f7b9550f12ad620508ba6ad7.py │ ├── aaf4257517b6eec8c7ce9c6dddcd9f62211789ca1b3d76dcced0ba63d2f28efe.py │ ├── 912c50a461239d714485c5118fec147aadb51a0d75ec4f0aafb6d19f2afb6f72.py │ ├── e999b38720143cf828dc40bbedadbb00c47ad0c145e218afce81572beed828da.py │ ├── 2eb7f1c54f705c4a7e69d9ced86a37c4df4f16a2ed625d8a9293e14f5c9c7eaf.py │ ├── ed2c2099cc5d8fe63005e937fcb54dea9556738234f4a47d366c930e26e1c927.py │ ├── d36ca6e382ec7eae65f0b5f3188a44a5f6907081e2320462cf9eddccc442bae7.py │ ├── 10a167203207604e7842f7f236a111e2bbd6f6adf4fcf54aa6d44e1aa12f2dbf.py │ ├── 3c8553171632b4e2697b855c004e42c6698d8c8e10e027bebfcb5153675b13ef.py │ ├── b579a43bd4e62c6db096e05362a80a9f7375bf2540f3afae1aeec73f00ab97b6.py │ ├── cb90061fd142abebb059244a45dd0581bdbed53772e47225e02db4d6c6064fda.py │ ├── dabf54a90b94ef885de18112fb2cffd4fa775496e4d09f93a1a4da792346e1e7.py │ ├── e6c6a6de2cc5595d987d42eabe19240d01a9e4e3e1405c78bea6d3f230a86467.py │ ├── 32f3dc1713079ad10291882238fdd2c7728acc1bf87c2b7fad1bd5dce4ebcf23.py │ ├── c8a7ea737ead966bbcb84c887d988813255c0a896f44c89d5bd9b2c4bbc2b7db.py │ ├── 590296c41506773455dc438ce7e9e3247d8a3ab58cf08c64bb4f04083b105313.py │ ├── d25777a706dfd8ace8628dc747fc1062c947bfa842940811420c36c7c98a1fd8.py │ ├── 2875d3f318bfc40137965386f3a131171f090ba91ab5341a30bde2dd3eef2dc0.py │ ├── fb5461b97acb09a942615d76f8a4368e0c6d132deb911e4f34a3821ba79dee37.py │ ├── 071758a387f5e3f792a7c7467d87b95daa9b285d991548b7a3c5f219fac053ce.py │ ├── 5cfb2149b2fe84094a2b4592b31ad39bd6206d679975a36efe819ac652b063ce.py │ ├── 3f2c13d405363a39811512559a7dc05a0d7c8d2d2b397697e3b303afa5868c6c.py │ ├── fcb03f9b44ce1c77e3553d8433f7a9e305a57e0e166b013551bddb247931ee26.py │ ├── 1413053e9794164120c397358390f4ff9f280ceef1d623b2fad4a87e6f932c59.py │ ├── f95da16611dd6fe6c892a1be1b6a1c7b9297f66c72d7580ef81f90aaad49f8ca.py │ ├── ed50cc7f9a66f3f6d37790c9bbc5daf3be88c7fa1304e972ed44bb08c277c0cb.py │ ├── 9e4e0f8cc79c918465feb8acd3d146e5aca1f14bf27eb26d99d2a57eb0e4dca9.py │ ├── b0698ccf6761f67239761ce27f74f6800c2c9da17b913e39823a26c34e8b74a9.py │ ├── d4e35786829b1c54d9492ff7a3eb6689faeaf4b9e559359f500c7062a0990e5a.py │ ├── 13f0a847a3e6c83ed61e8e4caf058d1bdafa60352c0279c7b0dfd2e412b66224.py │ ├── ee06f0cef6ed0c54cf617760a109a7d2a796b8513960153038a9c8383fc97ed3.py │ ├── 1db51410f27fe100fcfc10212cb4070d8c54bf15f399df430c1192f61a710cd1.py │ ├── 4c1e530d2c379853a4b1c0917c53e69bb39a842e83b77576e3627842d6354672.py │ ├── 6aec1c1ffde5c4d0ff20ad461e532db4bfbb892a95b89fd306d9d3605f6eacf9.py │ ├── 544887cb7d6d057d5d1962ac235882167a5a5a4439a234f2f50bfd9c1a4dfd86.py │ ├── ad9221c70ea548d130ca84aed1bfe1eaae37693b14a654272842d6c886e6a920.py │ ├── d065d738a49528171cadadb911340474ebb7cb25b74653fa6f76383b5b39c267.py │ ├── 3ee08d92317acdfb57bcd1a2fe9a6e9c7dad7aa337ed1ca879ff72922c94ff2a.py │ ├── ecdff53cb488c4b0a95d1194691f4ed13a81ed47ae1d5525a18ec045c3740a88.py │ ├── aec9d24bbe842109d37324991ad4e729fd4cda3434180bd85ef5e5e6b741da77.py │ ├── 0bff03cb20bbb5235e7a1f4224a855bea7203bd4a872777f8aacf947f2aa9163.py │ ├── a741bd1c5bfa9a9f7a630b1ef5b00ddd5e5f952c11c99946827560a7ff739318.py │ ├── bf4dd4d22a1f2bb85fa42bbb3c18ba8fc6c5115b7f5a0c36a0c7474d131c1a9a.py │ ├── 72dc7899219471ef8a847f9ca2129cba5f63f9c81a9e79293491c727bfbe2867.py │ ├── e9f4db361a1f95b628548012eb4fcd30d9b5df81722e245e47f4c23f4f25c2c7.py │ ├── 2b3d3db96b4b46a0f97cb5c664a614584cbc7f7439db4d8cd17d42820c741635.py │ ├── fc379af3330f835cf6d80f06c7a90625dad0b23588e17ca8b7d64ec7004a2b0e.py │ ├── 4e4e76d27980319b522279cca8d9f44a49f276635b08ac999eb17a24e9037b83.py │ ├── c6deaccaac47a6e3c5aeb86d03df0ba8aafeef0035da0875935835a8d92979c1.py │ ├── 22d7e90d8e6830c2b773a687048fee622d52f9a483748d228f446062b110c9cc.py │ ├── 536d989999e06d3d084d8dc3f3b5f9534f80edaa792a12d6982555135497d870.py │ ├── 7a47b8156beea57004fd29228b00571faeef3226f75049521bb76c53fd0cd31c.py │ ├── b6b6c0d5ded78f9f06861c44ebd58dde9bdf0d4ce708d8344d48b164321196a8.py │ ├── e030558ac8d9ce0385686711b5b3cc6f0fec95103400f786ad8bfdf5327b0880.py │ ├── 9051062853b2853e1ee23e859e808ed7e9cbce367fdd703c694db7bf631b3b63.py │ ├── a005252def05828fe471eec5daef145d3332a2935b0c317f596ce6118e017682.py │ ├── a9331f90fa3c39247dfa99a8412c105e66cfa7a61b3c253728225042098676d6.py │ ├── aefa14a91815738f75c920c0d2d2ecf8725c0ccbee291ef24b6f3d0a9b78dbb2.py │ ├── 70bbc09cf8a083e05fd35d0b20f3cab4b9aca486d136097f3fe7cbebd6f07bae.py │ ├── 0ef0cad747ad103e36fc87402a82b452cdc2846882c1795705672e226b8781cd.py │ ├── 8499312d9638cfdaf5040898d911d9642959e830c4acef3c9bdb89cf676b6d5c.py │ ├── 6adfa013a6d77e2d3724197af1ef899b3d909c7b22bb128010a03d4736a11986.py │ └── 869b220024e2220d80c6777187ef1e6f1f424ed4a5b2b84f8463eaf09e3642a0.py ├── TestBase.py ├── conftest.py ├── test_valid_source.py ├── test_invalid_ast.py └── test_fix_nonlocal.py ├── pysource_codegen ├── py.typed ├── __init__.py ├── __about__.py ├── _utils.py ├── types.py ├── _limits.py ├── __main__.py └── ast_info.py ├── .github ├── FUNDING.yml └── workflows │ ├── cog.yml │ └── ci.yml ├── .gitignore ├── LICENSE ├── find_new_issue.py ├── .pre-commit-config.yaml ├── pyproject.toml ├── README.md ├── run_all.py └── CHANGELOG.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pysource_codegen/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 15r10nk 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/valid_source_samples/e49b5c7a9e738d485bc77b54d9906a928859295bcd805a4600efcb51f1588e9e.py: -------------------------------------------------------------------------------- 1 | raise 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/efa099f3f0e0f121092ccc80748e056d1950b3f293f48d1dfa16a76b25afe143.py: -------------------------------------------------------------------------------- 1 | f"f'" 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/412b0c5f596fee0b7014653e45a3199feac3a4af1ebe3a37b6d37ed0ee02b461.py: -------------------------------------------------------------------------------- 1 | del foo().e 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/ace08d0effbd437bc88615a8851496dc6eee9e5abb5f9dc3e977ba84f7ab3592.py: -------------------------------------------------------------------------------- 1 | del foo()[1] 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/193baa3771b0bd2bbedb024ebd6251a3c520ea76dbf5fc0250866a15dcbba9a1.py: -------------------------------------------------------------------------------- 1 | del [something] 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/622304cbf3785ec23201b087777987082588575bf1a655cb149b5b8265cb5880.py: -------------------------------------------------------------------------------- 1 | lambda: (yield) 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/8201900a0c7ad514550cea8bbf502fc5999e7ded8b5d6188a1d6398a294fed4b.py: -------------------------------------------------------------------------------- 1 | del (_DAYNAMES,) 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/1cef236c84cad928164392a320d63d433857e8948d5093d54a4f8ef9c98d2093.py: -------------------------------------------------------------------------------- 1 | name_0: lambda: name_3 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/5b89856e57891de7a2520769129ce1f62b163a4fc99f17cec0a7b43dc626a9e5.py: -------------------------------------------------------------------------------- 1 | global a 2 | global a 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/5c2f9e9ccf5b6b88b118bdcdb0497214f9884f839b82d9a75da2577090f4ec47.py: -------------------------------------------------------------------------------- 1 | global name_0, name_0 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/a6e3d966873ac026adfc99771d14e0582e4c5464736128a89c6828dcad1f01e9.py: -------------------------------------------------------------------------------- 1 | del _squeue[something] 2 | -------------------------------------------------------------------------------- /pysource_codegen/__init__.py: -------------------------------------------------------------------------------- 1 | from ._codegen import generate 2 | 3 | __all__ = ("generate",) 4 | 5 | __version__ = "0.7.1" 6 | -------------------------------------------------------------------------------- /tests/valid_source_samples/24073542e630cba05794fb436ff8e55801c06f33656a2090e816f12dbb66bf29.py: -------------------------------------------------------------------------------- 1 | (equal_ast for l, r in zip) 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/ad8aa993e6ee4eb5ee764d55f2e3fd636a99b2ecb8c5aff2b35fbb78a074ea30.py: -------------------------------------------------------------------------------- 1 | (i async for i in arange) 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/fcba90c741e667a0c674c9b2ef5c6486e4ebff34bf52bed5780be3cd9164c941.py: -------------------------------------------------------------------------------- 1 | self._bin_dirs: List = [] 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/05bdd936a1c7835b41d94fd1ae8b87c39cea1754aa9bb217d22aeb2d78032472.py: -------------------------------------------------------------------------------- 1 | raise NothingChanged from None 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/15ef8f5d16cd5adb4f571c4e6e261c26a1b9b93f56901e59d21241b3fa9f7e07.py: -------------------------------------------------------------------------------- 1 | del _squeue[operator.indexOf()] 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/3746199c279c8163acf64ad9bc2f223a79c438c2198988bb5012111e38f44f8d.py: -------------------------------------------------------------------------------- 1 | name_2[name_1]: name_1 = name_0 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/360c8863298603afc729c3b81eaaa3bd4b49529bb3f5818c33036fefe960733d.py: -------------------------------------------------------------------------------- 1 | for file in something: 2 | continue 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/5f0d8da5e2b7ab1505151a242130539632fb8e4e1d2d3a8b2df3c24e035610f0.py: -------------------------------------------------------------------------------- 1 | (await {} async for name_1 in name_5) 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/8d9b3e8ebe7bdd8ce31cdb3a8441fe20564dc087ee1901876e7259d533c68282.py: -------------------------------------------------------------------------------- 1 | class name_1(*name_3): 2 | pass 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/7113d3b0d32e5bcc8109106f6bcbfdb82a5ef254706752f1a37084ccae1e5de5.py: -------------------------------------------------------------------------------- 1 | match 0: 2 | case 0: 3 | pass 4 | -------------------------------------------------------------------------------- /tests/valid_source_samples/b4f38ff898ca652e599bc5927989f3afe68283f7bbd1eea01624e90b39f53410.py: -------------------------------------------------------------------------------- 1 | """from setuptools import setup; setup()""" 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/e76d44e9af60d7d5d9228f80b96af8efc673ef861bf1683360162ea6551226e8.py: -------------------------------------------------------------------------------- 1 | match x: 2 | case -0: 3 | pass 4 | -------------------------------------------------------------------------------- /tests/valid_source_samples/2feb13c351ec7534080b009d102f5bf1e6c321eb6fb4253bbb10e236b80fb0ca.py: -------------------------------------------------------------------------------- 1 | def f(x): 2 | class c: 3 | nonlocal x 4 | -------------------------------------------------------------------------------- /tests/valid_source_samples/3f7aaaf6a910fcca6ae7e096af1c80769114c0ef6abd1e531bdc0f082b2e8004.py: -------------------------------------------------------------------------------- 1 | global RELATIVE_DIR, CANONICAL_FILENAME_CACHE 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/e02b69c37d775f00b3682c8ee9094fa145cb1ece8166ad5cbac720888308413c.py: -------------------------------------------------------------------------------- 1 | class name_0(name_1 := something): 2 | pass 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/82f80c15eaabb0f140d7bc1690a1cef1ac9c600a0e089fdc10b4aa8e8d477fe6.py: -------------------------------------------------------------------------------- 1 | type name_0[name_1: [name_2 for name_3 in name_3]] = name_4 2 | -------------------------------------------------------------------------------- /tests/valid_source_samples/1d98049a3f80c5a41355f65ad2fdddcffc7d48090740133f832eb24ef825d912.py: -------------------------------------------------------------------------------- 1 | class TestSuper: 2 | class X: 3 | nonlocal __class__ 4 | -------------------------------------------------------------------------------- /tests/valid_source_samples/01ccb7937a32d6d5f0198c24825be34428c24a539d127a2954c3cd837f319120.py: -------------------------------------------------------------------------------- 1 | def name_0(name_1: (name_2 for name_5 in name_5), /): 2 | pass 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/4db28b9b8f61b1f776338a1fa25d1b424a3c5c3803acd069d16e233a98dfa28e.py: -------------------------------------------------------------------------------- 1 | class name_1({name_0: name_1 for name_1 in name_5}): 2 | pass 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/bac11acb665178bb107402a47c0eed8e58175552d74f41b3870f6617fbc03c6c.py: -------------------------------------------------------------------------------- 1 | class TestSuper: 2 | def tearDown(): 3 | nonlocal __class__ 4 | -------------------------------------------------------------------------------- /tests/valid_source_samples/aeba483abfb424f27b4c6bf11671e54d2db676635607f32724e2b1bc34a7b24b.py: -------------------------------------------------------------------------------- 1 | match 0: 2 | case x if x: 3 | pass 4 | case _: 5 | pass 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .hypothesis/ 2 | __pycache__ 3 | .python-version 4 | .coverage 5 | dist 6 | .coverage* 7 | .mutmut-cache 8 | venv* 9 | checked_valid_source_files 10 | uv.lock 11 | -------------------------------------------------------------------------------- /pysource_codegen/__about__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024-present Frank Hoffmann <15r10nk-git@polarbit.de> 2 | # 3 | # SPDX-License-Identifier: MIT 4 | __version__ = "0.5.2" 5 | -------------------------------------------------------------------------------- /tests/valid_source_samples/d35eb119be1cd86d5059f9fe44def6770f23e2a774a53c596abe3242a523a718.py: -------------------------------------------------------------------------------- 1 | async def name_4(name_2: {name_5 for name_1()[{*()}] in something}, /): 2 | pass 3 | -------------------------------------------------------------------------------- /tests/valid_source_samples/ec595d73209619a769a17cf5a90de184b7a414b0d9ddd473dd9388df8a77944c.py: -------------------------------------------------------------------------------- 1 | def test_frame_resurrect(): 2 | def gen(): 3 | nonlocal frame 4 | 5 | del frame 6 | -------------------------------------------------------------------------------- /tests/valid_source_samples/40b181724590fa49314c1ec128d415b9eebc1b89a756869972303ee344548b16.py: -------------------------------------------------------------------------------- 1 | def testNonlocal(): 2 | x = 0 3 | 4 | def f(): 5 | nonlocal x 6 | nonlocal x 7 | -------------------------------------------------------------------------------- /tests/valid_source_samples/15d11c996d51cad39acbf52f2de302edfff89564e9c644fb24a6fbc1d71ad74f.py: -------------------------------------------------------------------------------- 1 | def _get_assertion_exprs(): 2 | lines: List = [] 3 | 4 | def _write_and_reset(): 5 | nonlocal lines 6 | -------------------------------------------------------------------------------- /tests/valid_source_samples/e58893bc48a383fa027aef9d985a26bc2eac09e57a4b4f6b92c4bb940eee5a0e.py: -------------------------------------------------------------------------------- 1 | async def leave_unfinalized_asyncgen(): 2 | async def agen(): 3 | yield item 4 | 5 | return status 6 | -------------------------------------------------------------------------------- /tests/valid_source_samples/4514e678724a6124c3dac5883c4bc0305e080f7b8928f9ce30ad1ca4e29b724c.py: -------------------------------------------------------------------------------- 1 | def test_async_gen_asyncio_shutdown_02(): 2 | async def main(): 3 | async for i in it: 4 | break 5 | -------------------------------------------------------------------------------- /tests/valid_source_samples/cf3df6db1ef8d4b989e4177d63a93907a3f168fcc0a74ac9aceecf20ca274c1b.py: -------------------------------------------------------------------------------- 1 | async def test_lock(): 2 | with something: 3 | 4 | def acquire_lock(): 5 | return (yield from lock) 6 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d5d29005017d00b30bd6f192bf87b2f6406e6cd5af0c847d6df01607eb9e1017.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Module 3 | from ast import Set 4 | 5 | tree = Module(body=[Expr(value=Set(elts=[]))], type_ignores=[]) 6 | 7 | # version: 3.8.16 8 | # seed = 8492326 9 | # 10 | # 11 | # Error: 12 | # AssertionError() 13 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/3ea606b5c05ede738f7f7c33dc9ad02e0dfe9da2ab12e44c9bea55e10afa4be1.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | 3 | tree = Module(body=[Delete(targets=[])], type_ignores=[]) 4 | 5 | # version: 3.9.15 6 | # seed = 4634023 7 | # 8 | # Source: 9 | # del 10 | # 11 | # 12 | # Error: 13 | # SyntaxError('invalid syntax', ('', 1, 5, 'del \n')) 14 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/405ad40048c979d08fac3374dd3305256f4a1918672e9fdf609e50da9ea08224.py: -------------------------------------------------------------------------------- 1 | from ast import Break 2 | from ast import Module 3 | 4 | tree = Module(body=[Break()], type_ignores=[]) 5 | 6 | # version: 3.12.0 7 | # 8 | # Source: 9 | # break 10 | # 11 | # 12 | # Error: 13 | # SyntaxError("'break' outside loop", ('', 1, 1, None, 1, 6)) 14 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/fb5b7e98a81fac64880a9521f0af8887d739fd1ad60b8d56006eece46cc42305.py: -------------------------------------------------------------------------------- 1 | from ast import Module 2 | from ast import Return 3 | 4 | tree = Module(body=[Return()], type_ignores=[]) 5 | 6 | # version: 3.12.0 7 | # 8 | # Source: 9 | # return 10 | # 11 | # 12 | # Error: 13 | # SyntaxError("'return' outside function", ('', 1, 1, None, 1, 7)) 14 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/dae694f70daffbafb3644d1800924e139266d9b86f9d5bd4d0c519a3fe5c49fb.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | 3 | tree = Module(body=[Delete(targets=[Yield()])], type_ignores=[]) 4 | 5 | # version: 3.12.0 6 | # 7 | # Source: 8 | # del (yield) 9 | # 10 | # 11 | # Error: 12 | # SyntaxError('cannot delete yield expression', ('', 1, 6, 'del (yield)\n', 1, 11)) 13 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/00d09cb4bde4e8e66bef022dfa684fa3e11b6cdf49e361d591a4799244ab70ca.py: -------------------------------------------------------------------------------- 1 | from ast import Import 2 | from ast import Module 3 | 4 | tree = Module(body=[Import(names=[])], type_ignores=[]) 5 | 6 | # version: 3.9.15 7 | # seed = 4634023 8 | # 9 | # Source: 10 | # import 11 | # 12 | # 13 | # Error: 14 | # SyntaxError('invalid syntax', ('', 1, 8, 'import \n')) 15 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/8e31e112e57521da11f8c8fc22d6793c7eaba0da4093e272de02fe22de9d83c4.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Module 3 | from ast import Slice 4 | 5 | tree = Module(body=[Expr(value=Slice())], type_ignores=[]) 6 | 7 | # version: 3.12.0 8 | # 9 | # Source: 10 | # : 11 | # 12 | # 13 | # Error: 14 | # SyntaxError('invalid syntax', ('', 1, 1, ':\n', 1, 2)) 15 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/57b88e20155b552e2db2f16eb52816bbe33d54239511c714d6f554d61176de09.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Module 3 | from ast import Yield 4 | 5 | tree = Module(body=[Expr(value=Yield())], type_ignores=[]) 6 | 7 | # version: 3.12.0 8 | # 9 | # Source: 10 | # yield 11 | # 12 | # 13 | # Error: 14 | # SyntaxError("'yield' outside function", ('', 1, 1, None, 1, 6)) 15 | -------------------------------------------------------------------------------- /pysource_codegen/_utils.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import sys 3 | from typing import Dict 4 | 5 | if sys.version_info >= (3, 9): 6 | from ast import unparse 7 | else: 8 | from astunparse import unparse # type: ignore 9 | 10 | 11 | def only_if(condition: bool, **kwargs) -> Dict: 12 | return kwargs if condition else {} 13 | 14 | 15 | def ast_dump(node): 16 | return ast.dump(node, **only_if(sys.version_info >= (3, 9), indent=2)) 17 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/a7d9ae6980afa14378caef3b2590be38e03a322b9106538e92ed896ed9bb926c.py: -------------------------------------------------------------------------------- 1 | from ast import Module 2 | from ast import Pass 3 | from ast import With 4 | 5 | tree = Module(body=[With(items=[], body=[Pass()])], type_ignores=[]) 6 | 7 | # version: 3.9.15 8 | # seed = 4634023 9 | # 10 | # Source: 11 | # with : 12 | # pass 13 | # 14 | # 15 | # Error: 16 | # SyntaxError('invalid syntax', ('', 1, 6, 'with :\n')) 17 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/9e0944875f10490b5ac4a9f3d9d545f09d5e58fd18887e44f47e1a0f59ddac12.py: -------------------------------------------------------------------------------- 1 | from ast import ImportFrom 2 | from ast import Module 3 | 4 | tree = Module(body=[ImportFrom(module="name_5", names=[], level=0)], type_ignores=[]) 5 | 6 | # version: 3.9.15 7 | # seed = 4634023 8 | # 9 | # Source: 10 | # from name_5 import 11 | # 12 | # 13 | # Error: 14 | # SyntaxError('invalid syntax', ('', 1, 20, 'from name_5 import \n')) 15 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/53a16f126fca2bf9dd710bf96c71c070ceb18f1fab1111bafddc5ac8b82e576f.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Expr 3 | from ast import JoinedStr 4 | from ast import Module 5 | 6 | tree = Module( 7 | body=[Expr(value=JoinedStr(values=[Constant(value=None)]))], type_ignores=[] 8 | ) 9 | 10 | # version: 3.12.0 11 | # 12 | # 13 | # Error: 14 | # ValueError('Unexpected node inside JoinedStr, ') 15 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/cd92d1c9ed898874c4ae2eca3d0789ed5eff628fe184b97fba6e6a0ddf1aa1a8.py: -------------------------------------------------------------------------------- 1 | from ast import Assign 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | 6 | tree = Module( 7 | body=[Assign(targets=[], value=Name(id="name_1", ctx=Load()))], type_ignores=[] 8 | ) 9 | 10 | # version: 3.12.0 11 | # seed = 2914913 12 | # 13 | # Source: 14 | # name_1 15 | # 16 | # 17 | # Error: 18 | # ValueError('empty targets on Assign') 19 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c4bcf0c9d029faaa9e471d953ae30cb9e75e4a44956a380058c9a523f1b72e75.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | 3 | tree = Module( 4 | body=[ 5 | Delete(targets=[Call(func=Name(id="name_5", ctx=Load()), args=[], keywords=[])]) 6 | ], 7 | type_ignores=[], 8 | ) 9 | 10 | # version: 3.12.0 11 | # 12 | # Source: 13 | # del name_5() 14 | # 15 | # 16 | # Error: 17 | # SyntaxError('cannot delete function call', ('', 1, 5, 'del name_5()\n', 1, 13)) 18 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/2bd68197b059e2fa32971c238286d02e97c73eab57c32db0a594d6af8c4ecb08.py: -------------------------------------------------------------------------------- 1 | from ast import Module 2 | from ast import Pass 3 | from ast import Try 4 | 5 | tree = Module( 6 | body=[Try(body=[Pass()], handlers=[], orelse=[], finalbody=[])], type_ignores=[] 7 | ) 8 | 9 | # version: 3.9.15 10 | # seed = 4634023 11 | # 12 | # Source: 13 | # try: 14 | # pass 15 | # 16 | # 17 | # Error: 18 | # SyntaxError('unexpected EOF while parsing', ('', 2, 9, ' pass\n')) 19 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/31ca384bae299cab7bf96ad606b3383a0d6df0acd8ead1b7ae1cab38eae6ab12.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import JoinedStr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[Expr(value=JoinedStr(values=[Name(id="name_0", ctx=Load())]))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # 15 | # Error: 16 | # ValueError('Unexpected node inside JoinedStr, ') 17 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/56e2e8a599b5dad0b7ba95940875950926e8306360de8b4c3a03605cf02a983c.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import JoinedStr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[Expr(value=JoinedStr(values=[Name(id="name_2", ctx=Load())]))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # 15 | # Error: 16 | # ValueError('Unexpected node inside JoinedStr, ') 17 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/9aaae4210d9083b69f97ecaa5823c1cd9a1937a8ae06a26407c3755c25acebc7.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import JoinedStr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[Expr(value=JoinedStr(values=[Name(id="name_0", ctx=Load())]))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # 15 | # Error: 16 | # ValueError('Unexpected node inside JoinedStr, ') 17 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/aa0faa8978ee0d835e4169c35ea1521e7035a70bf0b0614041da3ede2016fcb8.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import JoinedStr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[Expr(value=JoinedStr(values=[Name(id="name_0", ctx=Load())]))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # 15 | # Error: 16 | # ValueError('Unexpected node inside JoinedStr, ') 17 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/4c7eea039f102b91ce5dc6c478a59be47743cc32eb93e4a10b2ed22eb165eab9.py: -------------------------------------------------------------------------------- 1 | from ast import Await 2 | from ast import Expr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[Expr(value=Await(value=Name(id="name_0", ctx=Load())))], type_ignores=[] 9 | ) 10 | 11 | # version: 3.12.0 12 | # 13 | # Source: 14 | # await name_0 15 | # 16 | # 17 | # Error: 18 | # SyntaxError("'await' outside function", ('', 1, 1, None, 1, 13)) 19 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/70761b2829f2bc6774b0ad7a8d3127729565ee384f4a78e1e3502a970682fde7.py: -------------------------------------------------------------------------------- 1 | from ast import Module 2 | from ast import Pass 3 | from ast import TryStar 4 | 5 | tree = Module( 6 | body=[TryStar(body=[Pass()], handlers=[], orelse=[], finalbody=[])], type_ignores=[] 7 | ) 8 | 9 | # version: 3.12.0 10 | # seed = 3021191 11 | # 12 | # Source: 13 | # try: 14 | # pass 15 | # 16 | # 17 | # Error: 18 | # SyntaxError("expected 'except' or 'finally' block", ('', 2, 9, ' pass\n', 2, -1)) 19 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/08c8e43f21d07d8d79668106ab21923fd39fff654bf79f957ec8d1c2d1d33b2a.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import YieldFrom 6 | 7 | tree = Module( 8 | body=[Expr(value=YieldFrom(value=Name(id="name_1", ctx=Load())))], type_ignores=[] 9 | ) 10 | 11 | # version: 3.12.0 12 | # 13 | # Source: 14 | # yield from name_1 15 | # 16 | # 17 | # Error: 18 | # SyntaxError("'yield' outside function", ('', 1, 1, None, 1, 18)) 19 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/811b5754f3a5636dcac40d0e5f6dca5d4b09324875b9217b6f1d366a98a99d02.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import Starred 6 | 7 | tree = Module( 8 | body=[Expr(value=Starred(value=Name(id="name_0", ctx=Load()), ctx=Load()))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # Source: 15 | # *name_0 16 | # 17 | # 18 | # Error: 19 | # SyntaxError("can't use starred expression here", ('', 1, 1, None, 1, 8)) 20 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/7fb058ec8db27618d6902b5374691db79e85bf981ee266f1e56075fcd54ccc75.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Module 3 | from ast import Name 4 | from ast import Starred 5 | from ast import Store 6 | 7 | tree = Module( 8 | body=[Expr(value=Starred(value=Name(id="name_3", ctx=Store()), ctx=Store()))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # Source: 15 | # *name_3 16 | # 17 | # 18 | # Error: 19 | # SyntaxError("can't use starred expression here", ('', 1, 1, None, 1, 8)) 20 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c2d9be1ca1ecf5bd343ebc53651fe24a32d50eb52bb8950f041fc20169d33460.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Module 3 | from ast import Name 4 | from ast import Starred 5 | from ast import Store 6 | 7 | tree = Module( 8 | body=[Expr(value=Starred(value=Name(id="name_1", ctx=Store()), ctx=Store()))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # Source: 15 | # *name_1 16 | # 17 | # 18 | # Error: 19 | # SyntaxError("can't use starred expression here", ('', 1, 1, None, 1, 8)) 20 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/73a79ca089e41554f52abac852d999fd1c8b6e0d87243cd1d03ac6fb2a066a8f.py: -------------------------------------------------------------------------------- 1 | from ast import Assign 2 | from ast import Constant 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[Assign(targets=[Constant(value=True)], value=Name(id="name_4", ctx=Load()))], 9 | type_ignores=[], 10 | ) 11 | 12 | # version: 3.12.0 13 | # 14 | # Source: 15 | # True = name_4 16 | # 17 | # 18 | # Error: 19 | # SyntaxError('cannot assign to True', ('', 1, 1, 'True = name_4\n', 1, 5)) 20 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/7bc6ba4645ea543c31ce117591dda826529c17d1d5d7aaf10edca103b96cfaa4.py: -------------------------------------------------------------------------------- 1 | from ast import Match 2 | from ast import Module 3 | from ast import Name 4 | from ast import Store 5 | 6 | tree = Module( 7 | body=[Match(subject=Name(id="name_5", ctx=Store()), cases=[])], type_ignores=[] 8 | ) 9 | 10 | # version: 3.10.8 11 | # seed = 4737038 12 | # 13 | # Source: 14 | # match name_5: 15 | # 16 | # 17 | # Error: 18 | # IndentationError("expected an indented block after 'match' statement on line 1", ('', 1, 14, 'match name_5:\n', 1, -1)) 19 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/90186e3f0182378d8e86f963dd01fdb315ec8de19d2c9fff086986d7f64f88e4.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[ 9 | Expr(value=FormattedValue(value=Name(id="name_1", ctx=Load()), conversion=115)) 10 | ], 11 | type_ignores=[], 12 | ) 13 | 14 | # version: 3.12.0 15 | # 16 | # Source: 17 | # {name_1!s} 18 | # 19 | # 20 | # Error: 21 | # SyntaxError('invalid syntax', ('', 1, 8, '{name_1!s}\n', 1, 9)) 22 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/b342ea89f9e8d4e54ac09e1752bfa6f35d69a0788e7831bd2f5e2f238755d2f2.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[ 9 | Expr( 10 | value=FormattedValue( 11 | value=Name(id="name_0", ctx=Load()), conversion=-1, format_spec=None 12 | ) 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.8.16 19 | # 20 | # 21 | # Error: 22 | # AttributeError("'FormattedValue' object has no attribute 'values'") 23 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/1bcbddd3ebb0de15ab35ac4df52a5cbab624d9624ddcf5185223d8067b666e4c.py: -------------------------------------------------------------------------------- 1 | from ast import JoinedStr 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import TypeAlias 6 | 7 | tree = Module( 8 | body=[ 9 | TypeAlias( 10 | name=JoinedStr(values=[]), 11 | type_params=[], 12 | value=Name(id="name_1", ctx=Load()), 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # 20 | # Source: 21 | # type f'' = name_1 22 | # 23 | # 24 | # Error: 25 | # SyntaxError('invalid syntax', ('', 1, 6, "type f'' = name_1\n", 1, 8)) 26 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/a0e15b43dc1f959d9c2aa44a923d267480b3b03adf2bfe86e460ff8229c3aafd.py: -------------------------------------------------------------------------------- 1 | from ast import JoinedStr 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import TypeAlias 6 | 7 | tree = Module( 8 | body=[ 9 | TypeAlias( 10 | name=JoinedStr(values=[]), 11 | type_params=[], 12 | value=Name(id="name_5", ctx=Load()), 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # 20 | # Source: 21 | # type f'' = name_5 22 | # 23 | # 24 | # Error: 25 | # SyntaxError('invalid syntax', ('', 1, 6, "type f'' = name_5\n", 1, 8)) 26 | -------------------------------------------------------------------------------- /pysource_codegen/types.py: -------------------------------------------------------------------------------- 1 | import ast 2 | from dataclasses import dataclass 3 | from typing import Dict 4 | from typing import List 5 | from typing import Literal 6 | from typing import Tuple 7 | from typing import Type 8 | from typing import Union 9 | 10 | 11 | @dataclass 12 | class NodeType: 13 | fields: Dict[str, Tuple[str, Union[Literal["?"], Literal["*"], Literal[""]]]] 14 | ast_type: Type[ast.AST] 15 | 16 | 17 | @dataclass 18 | class BuiltinNodeType: 19 | kind: Union[ 20 | Literal["identifier"], Literal["int"], Literal["string"], Literal["constant"] 21 | ] 22 | 23 | 24 | @dataclass 25 | class UnionNodeType: 26 | options: List[str] 27 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/fcfb014cd6eef719639604562887fc22d165c684bc24fdbb00b6d1be818ce947.py: -------------------------------------------------------------------------------- 1 | from ast import Dict 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import TypeAlias 6 | 7 | tree = Module( 8 | body=[ 9 | TypeAlias( 10 | name=Name(id="name_4", ctx=Load()), 11 | type_params=[], 12 | value=Dict(keys=[], values=[]), 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # seed = 4378073 20 | # 21 | # Source: 22 | # type name_4 = {} 23 | # 24 | # 25 | # Error: 26 | # ValueError('expression must have Store context but has Load instead') 27 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/201fd1188d01b64e59912d50e9903e9c0cfd5e39b8716ea859e1e9aa506c5c62.py: -------------------------------------------------------------------------------- 1 | from ast import AugAssign 2 | from ast import Div 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[ 9 | AugAssign( 10 | target=Name(id="something", ctx=Load()), 11 | op=Div(), 12 | value=Name(id="name_2", ctx=Load()), 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # seed = 467504 20 | # 21 | # Source: 22 | # something /= name_2 23 | # 24 | # 25 | # Error: 26 | # ValueError('expression must have Store context but has Load instead') 27 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/eb58c698896405e41e912e403f8ea4fd66953b64cf5adde3a8988551d71e3324.py: -------------------------------------------------------------------------------- 1 | from ast import ClassDef 2 | from ast import Global 3 | from ast import Module 4 | from ast import Pass 5 | 6 | tree = Module( 7 | body=[ 8 | ClassDef( 9 | name="name_0", bases=[], keywords=[], body=[Pass()], decorator_list=[] 10 | ), 11 | Global(names=["name_0"]), 12 | ], 13 | type_ignores=[], 14 | ) 15 | 16 | # version: 3.10.8 17 | # 18 | # Source: 19 | # class name_0: 20 | # pass 21 | # global name_0 22 | # 23 | # 24 | # Error: 25 | # SyntaxError("name 'name_0' is assigned to before global declaration") 26 | # seed = 1826439 27 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/85e465ef7787fa3b3c32a4e1c7c49538ca87f1090438fabe9307ac221a0d0a73.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Module 3 | from ast import Name 4 | from ast import Set 5 | from ast import TypeAlias 6 | 7 | tree = Module( 8 | body=[ 9 | TypeAlias( 10 | name=Set(elts=[Name(id="name_1", ctx=Load())]), 11 | type_params=[], 12 | value=Name(id="name_3", ctx=Load()), 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # 20 | # Source: 21 | # type {name_1} = name_3 22 | # 23 | # 24 | # Error: 25 | # SyntaxError('invalid syntax', ('', 1, 6, 'type {name_1} = name_3\n', 1, 7)) 26 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/9202b8548441a2959111ed2169f659b7f13fd6f9f165c5fc8f067c733bf06111.py: -------------------------------------------------------------------------------- 1 | from ast import Assign 2 | from ast import Constant 3 | from ast import JoinedStr 4 | from ast import Module 5 | from ast import Store 6 | from ast import Tuple 7 | 8 | tree = Module( 9 | body=[ 10 | Assign( 11 | targets=[Tuple(elts=[JoinedStr(values=[])], ctx=Store())], 12 | value=Constant(value=False), 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # 20 | # Source: 21 | # f'', = False 22 | # 23 | # 24 | # Error: 25 | # SyntaxError('cannot assign to f-string expression', ('', 1, 1, "f'', = False\n", 1, 4)) 26 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c04e4881fbbd2d7a352b4a103a55f894a6c4e7e2299211521c38ea34d1165d26.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | 3 | tree = Module( 4 | body=[ 5 | Delete( 6 | targets=[ 7 | Compare( 8 | left=Name(id="name_5", ctx=Load()), 9 | ops=[GtE(), IsNot()], 10 | comparators=[Name(id="name_5", ctx=Load())], 11 | ) 12 | ] 13 | ) 14 | ], 15 | type_ignores=[], 16 | ) 17 | 18 | # version: 3.12.0 19 | # 20 | # Source: 21 | # del name_5 >= name_5 22 | # 23 | # 24 | # Error: 25 | # SyntaxError('cannot delete comparison', ('', 1, 5, 'del name_5 >= name_5\n', 1, 21)) 26 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c9106824d0e85940e71276123bd6bc082058789de30886c12a94e6c680eac9a5.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import ExtSlice 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import Subscript 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=Subscript( 12 | value=Name(id="name_1", ctx=Load()), slice=ExtSlice(dims=[]), ctx=Load() 13 | ) 14 | ) 15 | ], 16 | type_ignores=[], 17 | ) 18 | 19 | # version: 3.8.16 20 | # seed = 6348378 21 | # 22 | # Source: 23 | # 24 | # name_1[] 25 | # 26 | # 27 | # 28 | # Error: 29 | # SyntaxError('invalid syntax', ('', 2, 8, 'name_1[]\n')) 30 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/6d75350e42778cb8e1d1f10eee27a79dc00f6a3e40bacebefc7d490e662a1856.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import Slice 6 | 7 | tree = Module( 8 | body=[ 9 | Expr( 10 | value=Slice( 11 | lower=Name(id="name_2", ctx=Load()), 12 | upper=Name(id="name_0", ctx=Load()), 13 | step=Name(id="name_3", ctx=Load()), 14 | ) 15 | ) 16 | ], 17 | type_ignores=[], 18 | ) 19 | 20 | # version: 3.12.0 21 | # 22 | # Source: 23 | # name_2:name_0:name_3 24 | # 25 | # 26 | # Error: 27 | # SyntaxError('invalid syntax', ('', 1, 14, 'name_2:name_0:name_3\n', 1, 15)) 28 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/8fb26fb07a3731362c51e638f159532d803a5aa8d5b55d68332126d6f1f94692.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import Slice 6 | 7 | tree = Module( 8 | body=[ 9 | Expr( 10 | value=Slice( 11 | lower=Name(id="name_0", ctx=Load()), 12 | upper=Name(id="name_2", ctx=Load()), 13 | step=Name(id="name_2", ctx=Load()), 14 | ) 15 | ) 16 | ], 17 | type_ignores=[], 18 | ) 19 | 20 | # version: 3.12.0 21 | # 22 | # Source: 23 | # name_0:name_2:name_2 24 | # 25 | # 26 | # Error: 27 | # SyntaxError('invalid syntax', ('', 1, 14, 'name_0:name_2:name_2\n', 1, 15)) 28 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d8fe8ca9a8f439de6b8552a8b39ec22a7efca65bcadcbb9945cc01775b6d5af8.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Module 3 | from ast import Name 4 | from ast import NamedExpr 5 | from ast import Store 6 | from ast import TypeAlias 7 | 8 | tree = Module( 9 | body=[ 10 | TypeAlias( 11 | name=Name(id="name_0", ctx=Store()), 12 | value=NamedExpr( 13 | target=Name(id="name_2", ctx=Store()), 14 | value=Name(id="name_0", ctx=Load()), 15 | ), 16 | ) 17 | ] 18 | ) 19 | 20 | # version: 3.13.0b1 21 | # seed = 1933771 22 | # 23 | # Source: 24 | # type name_0 = (name_2 := name_0) 25 | # 26 | # 27 | # Error: 28 | # SyntaxError('named expression cannot be used within a type alias') 29 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/416736d2215d8e7fc07352649f7419d9569294aebab30b0caefc5d259007a6d6.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | 9 | tree = Module( 10 | body=[ 11 | Match( 12 | subject=Name(id="name_2", ctx=Load()), 13 | cases=[match_case(pattern=Constant(value=None), body=[Pass()])], 14 | ) 15 | ], 16 | type_ignores=[], 17 | ) 18 | 19 | # version: 3.12.0 20 | # seed = 8897260 21 | # 22 | # Source: 23 | # match name_2: 24 | # case None: 25 | # pass 26 | # 27 | # 28 | # Error: 29 | # TypeError('expected some sort of pattern, but got ') 30 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/49981c344fde47e1eb48aaefb32b9b6d944ba34a3f9db4d667c865af51c7dfcf.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Module 3 | from ast import Name 4 | from ast import NamedExpr 5 | from ast import TypeAlias 6 | 7 | tree = Module( 8 | body=[ 9 | TypeAlias( 10 | name=Name(id="name_0", ctx=Load()), 11 | type_params=[], 12 | value=NamedExpr( 13 | target=Name(id="name_2", ctx=Load()), 14 | value=Name(id="name_5", ctx=Load()), 15 | ), 16 | ) 17 | ], 18 | type_ignores=[], 19 | ) 20 | 21 | # version: 3.12.0 22 | # 23 | # Source: 24 | # type name_0 = (name_2 := name_5) 25 | # 26 | # 27 | # Error: 28 | # SyntaxError('named expression cannot be used within a type alias') 29 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/bd81263ead51ea209adf81302d2bc2fe4b3562c363e39111a61d9bac8f58d632.py: -------------------------------------------------------------------------------- 1 | from ast import AugAssign 2 | from ast import Load 3 | from ast import MatMult 4 | from ast import Module 5 | from ast import Name 6 | from ast import Store 7 | from ast import Tuple 8 | 9 | tree = Module( 10 | body=[ 11 | AugAssign( 12 | target=Tuple(elts=[Name(id="something", ctx=Load())], ctx=Load()), 13 | op=MatMult(), 14 | value=Name(id="name_2", ctx=Store()), 15 | ) 16 | ], 17 | type_ignores=[], 18 | ) 19 | 20 | # version: 3.12.0 21 | # 22 | # Source: 23 | # (something,) @= name_2 24 | # 25 | # 26 | # Error: 27 | # SyntaxError("'tuple' is an illegal expression for augmented assignment", ('', 1, 1, '(something,) @= name_2\n', 1, 13)) 28 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/80f13eb1d4cc2a045f57bba482a24090e67040a15dffa12479ec05077eb9e1aa.py: -------------------------------------------------------------------------------- 1 | from ast import BoolOp 2 | from ast import Expr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import NamedExpr 7 | from ast import Or 8 | 9 | tree = Module( 10 | body=[ 11 | Expr( 12 | value=NamedExpr( 13 | target=BoolOp(op=Or(), values=[Name(id="name_1", ctx=Load())]), 14 | value=Name(id="something", ctx=Load()), 15 | ) 16 | ) 17 | ], 18 | type_ignores=[], 19 | ) 20 | 21 | # version: 3.12.0 22 | # 23 | # Source: 24 | # ((name_1) := something) 25 | # 26 | # 27 | # Error: 28 | # SyntaxError('cannot use assignment expressions with name', ('', 1, 3, '((name_1) := something)\n', 1, 9)) 29 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/f74405488bd9786f83afb55c357ab2589c64db9a19984eab3d6c99de2700e511.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import Continue 4 | from ast import Module 5 | 6 | tree = Module( 7 | body=[ 8 | AsyncFunctionDef( 9 | name="name_2", 10 | args=arguments( 11 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 12 | ), 13 | body=[Continue()], 14 | decorator_list=[], 15 | type_params=[], 16 | ) 17 | ], 18 | type_ignores=[], 19 | ) 20 | 21 | # version: 3.12.0 22 | # 23 | # Source: 24 | # async def name_2(): 25 | # continue 26 | # 27 | # 28 | # Error: 29 | # SyntaxError("'continue' not properly in loop", ('', 2, 5, None, 2, 13)) 30 | -------------------------------------------------------------------------------- /pysource_codegen/_limits.py: -------------------------------------------------------------------------------- 1 | def calc_f_string_expr_limit(): 2 | n = 0 3 | s = "1" 4 | while True: 5 | for q in ("'", '"', '"""', "'''"): 6 | ns = "f" + q + "{" + s + "}" + q 7 | 8 | try: 9 | eval(ns) 10 | s = ns 11 | break 12 | except: 13 | continue 14 | else: 15 | return n 16 | n += 1 17 | 18 | 19 | def calc_f_string_format_limit(): 20 | n = 0 21 | s = "{1}" 22 | while True: 23 | s = "{2:" + s + "}" 24 | 25 | try: 26 | eval(f"f'{s}'") 27 | except: 28 | break 29 | n += 1 30 | 31 | return n 32 | 33 | 34 | f_string_expr_limit = calc_f_string_expr_limit() 35 | f_string_format_limit = calc_f_string_format_limit() 36 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/340f35d4e8b3e2aed11c02f04a3a9f0e6f600f480e675902b3f041be8b5df0c4.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Match 3 | from ast import match_case 4 | from ast import MatchSingleton 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | 9 | tree = Module( 10 | body=[ 11 | Match( 12 | subject=Name(id="name_2", ctx=Load()), 13 | cases=[ 14 | match_case(pattern=MatchSingleton(value=b"some bytes"), body=[Pass()]) 15 | ], 16 | ) 17 | ], 18 | type_ignores=[], 19 | ) 20 | 21 | # version: 3.12.0 22 | # seed = 8718137 23 | # 24 | # Source: 25 | # match name_2: 26 | # case b'some bytes': 27 | # pass 28 | # 29 | # 30 | # Error: 31 | # ValueError('MatchSingleton can only contain True, False and None') 32 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/b13adc212d95bd6b366b45ad5d8e5e7525d7edefc8e5f9a9fc62a60be83cd1b4.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import For 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import Pass 7 | from ast import Set 8 | from ast import Store 9 | from ast import Tuple 10 | 11 | tree = Module( 12 | body=[ 13 | For( 14 | target=Tuple(elts=[Set(elts=[Name(id="name_3", ctx=Load())])], ctx=Store()), 15 | iter=Constant(value=1), 16 | body=[Pass()], 17 | orelse=[], 18 | ) 19 | ], 20 | type_ignores=[], 21 | ) 22 | 23 | # version: 3.12.0 24 | # 25 | # Source: 26 | # for {name_3}, in 1: 27 | # pass 28 | # 29 | # 30 | # Error: 31 | # SyntaxError('cannot assign to set display', ('', 1, 5, 'for {name_3}, in 1:\n', 1, 13)) 32 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/42b9134add4c95799f19958d1476a8eaf748e6421332cd3449638f941c809e54.py: -------------------------------------------------------------------------------- 1 | from ast import Dict 2 | from ast import Expr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | 7 | tree = Module( 8 | body=[ 9 | Expr( 10 | value=Dict( 11 | keys=[ 12 | Name(id="name_5", ctx=Load()), 13 | Name(id="name_5", ctx=Load()), 14 | Name(id="name_4", ctx=Load()), 15 | Name(id="name_3", ctx=Load()), 16 | ], 17 | values=[], 18 | ) 19 | ) 20 | ], 21 | type_ignores=[], 22 | ) 23 | 24 | # version: 3.12.0 25 | # seed = 4378073 26 | # 27 | # Source: 28 | # {} 29 | # 30 | # 31 | # Error: 32 | # ValueError("Dict doesn't have the same number of keys as values") 33 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/5af501bb4a4f8582f357e34db766519c545e50f4d20713986b4ce2922dcdadd6.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import JoinedStr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=JoinedStr( 12 | values=[ 13 | FormattedValue( 14 | value=Name(id="name_4", ctx=Load()), 15 | conversion=115, 16 | format_spec=Name(id="name_0", ctx=Load()), 17 | ) 18 | ] 19 | ) 20 | ) 21 | ], 22 | type_ignores=[], 23 | ) 24 | 25 | # version: 3.12.0 26 | # 27 | # 28 | # Error: 29 | # ValueError('Unexpected node inside JoinedStr, ') 30 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/619370089c11db7565ceda70b818fccfaebdc1f8658068699f54053803aba62f.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import JoinedStr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=JoinedStr( 12 | values=[ 13 | FormattedValue( 14 | value=Name(id="name_3", ctx=Load()), 15 | conversion=114, 16 | format_spec=Name(id="name_4", ctx=Load()), 17 | ) 18 | ] 19 | ) 20 | ) 21 | ], 22 | type_ignores=[], 23 | ) 24 | 25 | # version: 3.12.0 26 | # 27 | # 28 | # Error: 29 | # ValueError('Unexpected node inside JoinedStr, ') 30 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c56942a0cb4b89e2b08ff2e4c6d93e6f60b2d960aa7b0174c7128d2f06f0e270.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import JoinedStr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=JoinedStr( 12 | values=[ 13 | FormattedValue( 14 | value=Name(id="name_4", ctx=Load()), 15 | conversion=115, 16 | format_spec=Name(id="name_3", ctx=Load()), 17 | ) 18 | ] 19 | ) 20 | ) 21 | ], 22 | type_ignores=[], 23 | ) 24 | 25 | # version: 3.12.0 26 | # 27 | # 28 | # Error: 29 | # ValueError('Unexpected node inside JoinedStr, ') 30 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d0b5e1927a82c7c2253bd4c27840023544dc9bd15eedd7a804e3e698ff1d6e77.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import JoinedStr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=JoinedStr( 12 | values=[ 13 | FormattedValue( 14 | value=Name(id="name_4", ctx=Load()), 15 | conversion=115, 16 | format_spec=Name(id="name_3", ctx=Load()), 17 | ) 18 | ] 19 | ) 20 | ) 21 | ], 22 | type_ignores=[], 23 | ) 24 | 25 | # version: 3.12.0 26 | # 27 | # 28 | # Error: 29 | # ValueError('Unexpected node inside JoinedStr, ') 30 | -------------------------------------------------------------------------------- /pysource_codegen/__main__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from ._codegen import generate 4 | 5 | 6 | def run(): 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument("--seed", type=int, help="seed value") 9 | parser.add_argument( 10 | "--node-limit", type=int, default=400, help="limit number of nodes" 11 | ) 12 | parser.add_argument( 13 | "--depth-limit", type=int, default=5, help="limit for the depth of the ast" 14 | ) 15 | parser.add_argument("--root-node", type=str, default="Module", help="root ast type") 16 | args = parser.parse_args() 17 | 18 | print( 19 | generate( 20 | args.seed, 21 | node_limit=args.node_limit, 22 | depth_limit=args.depth_limit, 23 | root_node=args.root_node, 24 | ) 25 | ) 26 | 27 | 28 | if __name__ == "__main__": 29 | run() 30 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/0e3cc3a70bb8ca79e3e6e21ad45fe7caa36411970ae2167a2d0a17b40c095646.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import AsyncWith 4 | from ast import Module 5 | from ast import Pass 6 | 7 | tree = Module( 8 | body=[ 9 | AsyncFunctionDef( 10 | name="name_2", 11 | args=arguments( 12 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 13 | ), 14 | body=[AsyncWith(items=[], body=[Pass()])], 15 | decorator_list=[], 16 | ) 17 | ], 18 | type_ignores=[], 19 | ) 20 | 21 | # version: 3.9.15 22 | # seed = 4634023 23 | # 24 | # Source: 25 | # async def name_2(): 26 | # async with : 27 | # pass 28 | # 29 | # 30 | # Error: 31 | # SyntaxError('invalid syntax', ('', 2, 16, ' async with :\n')) 32 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/54c0b446dc337abdc16c50d7928dd3c9cca98b6d8884d30d57aa1001c3142ff8.py: -------------------------------------------------------------------------------- 1 | from ast import ExceptHandler 2 | from ast import Expr 3 | from ast import Global 4 | from ast import Module 5 | from ast import Name 6 | from ast import Pass 7 | from ast import Store 8 | from ast import Try 9 | 10 | tree = Module( 11 | body=[ 12 | Try( 13 | body=[Pass()], 14 | handlers=[ExceptHandler(body=[Global(names=["name_0"])])], 15 | orelse=[Expr(value=Name(id="name_0", ctx=Store()))], 16 | finalbody=[], 17 | ) 18 | ], 19 | type_ignores=[], 20 | ) 21 | 22 | # version: 3.9.15 23 | # seed = 8315429 24 | # 25 | # Source: 26 | # try: 27 | # pass 28 | # except: 29 | # global name_0 30 | # else: 31 | # name_0 32 | # 33 | # 34 | # Error: 35 | # SyntaxError("name 'name_0' is used prior to global declaration") 36 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/567b705d091d5a43efcaf38c9980921e1ee819e1c33484d90308b96d57acb896.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import FunctionDef 3 | from ast import Global 4 | from ast import Module 5 | from ast import Nonlocal 6 | 7 | tree = Module( 8 | body=[ 9 | FunctionDef( 10 | name="name_1", 11 | args=arguments(), 12 | body=[ 13 | Global(names=["name_2"]), 14 | FunctionDef( 15 | name="name_2", args=arguments(), body=[Nonlocal(names=["name_2"])] 16 | ), 17 | ], 18 | ) 19 | ] 20 | ) 21 | 22 | # version: 3.13.0b1 23 | # seed = 2636981 24 | # 25 | # Source: 26 | # def name_1(): 27 | # global name_2 28 | # 29 | # def name_2(): 30 | # nonlocal name_2 31 | # 32 | # 33 | # Error: 34 | # SyntaxError("no binding for nonlocal 'name_2' found") 35 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/7db01336123c8c30720412ce816e728caa712a3541427f18796d9ee26195e498.py: -------------------------------------------------------------------------------- 1 | from ast import And 2 | from ast import AugAssign 3 | from ast import BoolOp 4 | from ast import Load 5 | from ast import LShift 6 | from ast import Module 7 | from ast import Name 8 | 9 | tree = Module( 10 | body=[ 11 | AugAssign( 12 | target=BoolOp( 13 | op=And(), 14 | values=[Name(id="name_1", ctx=Load()), Name(id="name_4", ctx=Load())], 15 | ), 16 | op=LShift(), 17 | value=Name(id="name_0", ctx=Load()), 18 | ) 19 | ], 20 | type_ignores=[], 21 | ) 22 | 23 | # version: 3.12.0 24 | # 25 | # Source: 26 | # name_1 and name_4 <<= name_0 27 | # 28 | # 29 | # Error: 30 | # SyntaxError("'expression' is an illegal expression for augmented assignment", ('', 1, 1, 'name_1 and name_4 <<= name_0\n', 1, 18)) 31 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/96417e0aac6381dc499c7697dcc092589a773523c1bb796285dd2a8181606826.py: -------------------------------------------------------------------------------- 1 | from ast import AnnAssign 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import Store 6 | from ast import Subscript 7 | 8 | tree = Module( 9 | body=[ 10 | AnnAssign( 11 | target=Subscript( 12 | value=Name(id="name_2", ctx=Load()), 13 | slice=Name(id="name_0", ctx=Load()), 14 | ctx=Store(), 15 | ), 16 | annotation=Name(id="name_4", ctx=Load()), 17 | value=Name(id="name_0", ctx=Load()), 18 | simple=1, 19 | ) 20 | ], 21 | type_ignores=[], 22 | ) 23 | 24 | # version: 3.12.0 25 | # seed = 2668094 26 | # 27 | # Source: 28 | # name_2[name_0]: name_4 = name_0 29 | # 30 | # 31 | # Error: 32 | # TypeError('AnnAssign with simple non-Name target') 33 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/72bc8ecc4a0486fb1bc845d6ff447542a77ffdd6c527fa229a3d0879938cac6f.py: -------------------------------------------------------------------------------- 1 | from ast import Compare 2 | from ast import Expr 3 | from ast import Load 4 | from ast import Lt 5 | from ast import Module 6 | from ast import Name 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=Compare( 12 | left=Name(id="name_1", ctx=Load()), 13 | ops=[Lt()], 14 | comparators=[ 15 | Name(id="name_1", ctx=Load()), 16 | Name(id="name_3", ctx=Load()), 17 | Name(id="name_3", ctx=Load()), 18 | ], 19 | ) 20 | ) 21 | ], 22 | type_ignores=[], 23 | ) 24 | 25 | # version: 3.12.0 26 | # seed = 4378073 27 | # 28 | # Source: 29 | # name_1 < name_1 30 | # 31 | # 32 | # Error: 33 | # ValueError('Compare has a different number of comparators and operands') 34 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/ed250c4b725d132c3f0a6515a1997c52f5fa9e20fca33ce90315de62d8bebe27.py: -------------------------------------------------------------------------------- 1 | from ast import Global 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import MatchSequence 6 | from ast import MatchStar 7 | from ast import Module 8 | from ast import Name 9 | 10 | tree = Module( 11 | body=[ 12 | Match( 13 | subject=Name(id="name_0", ctx=Load()), 14 | cases=[ 15 | match_case( 16 | pattern=MatchSequence(patterns=[MatchStar(name="name_5")]), 17 | body=[Global(names=["name_5"])], 18 | ) 19 | ], 20 | ) 21 | ] 22 | ) 23 | 24 | # version: 3.13.0b1 25 | # seed = 3678395 26 | # 27 | # Source: 28 | # match name_0: 29 | # case [*name_5]: 30 | # global name_5 31 | # 32 | # 33 | # Error: 34 | # SyntaxError("name 'name_5' is assigned to before global declaration") 35 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/e46612db2bd98f7426567240a230c786129e039d96c0f7d75a9484c5728b024b.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import Expr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import YieldFrom 8 | 9 | tree = Module( 10 | body=[ 11 | AsyncFunctionDef( 12 | name="name_2", 13 | args=arguments( 14 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 15 | ), 16 | body=[Expr(value=YieldFrom(value=Name(id="name_0", ctx=Load())))], 17 | decorator_list=[], 18 | type_params=[], 19 | ) 20 | ], 21 | type_ignores=[], 22 | ) 23 | 24 | # version: 3.12.0 25 | # 26 | # Source: 27 | # async def name_2(): 28 | # yield from name_0 29 | # 30 | # 31 | # Error: 32 | # SyntaxError("'yield from' inside async function", ('', 2, 5, None, 2, 22)) 33 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/634715912e059f073f55ba9d3d48d71a3d23cf0ff4b4bcfe24a442b1cd62475c.py: -------------------------------------------------------------------------------- 1 | from ast import AnnAssign 2 | from ast import Attribute 3 | from ast import Compare 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import Store 8 | 9 | tree = Module( 10 | body=[ 11 | AnnAssign( 12 | target=Attribute( 13 | value=Compare( 14 | left=Name(id="name_5", ctx=Load()), ops=[], comparators=[] 15 | ), 16 | attr="name_3", 17 | ctx=Store(), 18 | ), 19 | annotation=Name(id="name_4", ctx=Load()), 20 | simple=3, 21 | ) 22 | ], 23 | type_ignores=[], 24 | ) 25 | 26 | # version: 3.9.15 27 | # seed = 3589045 28 | # 29 | # Source: 30 | # (name_5).name_3: name_4 31 | # 32 | # 33 | # Error: 34 | # SyntaxError('illegal target for annotation', ('', 1, 1, '(name_5).name_3: name_4\n')) 35 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/15ae66a5d7d947c199b82be80107b035dcd47da0f7b9550f12ad620508ba6ad7.py: -------------------------------------------------------------------------------- 1 | from ast import Break 2 | from ast import ClassDef 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import While 7 | 8 | tree = Module( 9 | body=[ 10 | While( 11 | test=Name(id="name_0", ctx=Load()), 12 | body=[ 13 | ClassDef( 14 | name="name_4", 15 | bases=[], 16 | keywords=[], 17 | body=[Break()], 18 | decorator_list=[], 19 | type_params=[], 20 | ) 21 | ], 22 | orelse=[], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # while name_0: 32 | # 33 | # class name_4: 34 | # break 35 | # 36 | # 37 | # Error: 38 | # SyntaxError("'break' outside loop", ('', 4, 9, None, 4, 14)) 39 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/aaf4257517b6eec8c7ce9c6dddcd9f62211789ca1b3d76dcced0ba63d2f28efe.py: -------------------------------------------------------------------------------- 1 | from ast import Invert 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import Pass 6 | from ast import UnaryOp 7 | from ast import With 8 | from ast import withitem 9 | 10 | tree = Module( 11 | body=[ 12 | With( 13 | items=[ 14 | withitem( 15 | context_expr=Name(id="name_2", ctx=Load()), 16 | optional_vars=UnaryOp( 17 | op=Invert(), operand=Name(id="something", ctx=Load()) 18 | ), 19 | ) 20 | ], 21 | body=[Pass()], 22 | ) 23 | ], 24 | type_ignores=[], 25 | ) 26 | 27 | # version: 3.12.0 28 | # 29 | # Source: 30 | # with name_2 as ~something: 31 | # pass 32 | # 33 | # 34 | # Error: 35 | # SyntaxError('cannot assign to expression', ('', 1, 16, 'with name_2 as ~something:\n', 1, 26)) 36 | -------------------------------------------------------------------------------- /.github/workflows/cog.yml: -------------------------------------------------------------------------------- 1 | name: Weekly Cog Update 2 | 3 | on: 4 | schedule: 5 | - cron: 0 8 * * 5 # Every Friday at 08:00 UTC 6 | workflow_dispatch: 7 | 8 | jobs: 9 | update-cog: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: '3.11' 21 | 22 | - name: Install Hatch 23 | run: pip install hatch 24 | 25 | - name: Run cog update 26 | run: hatch run cog:update 27 | 28 | - name: Create Pull Request 29 | uses: peter-evans/create-pull-request@v6 30 | with: 31 | commit-message: 'chore: weekly cog update' 32 | title: 'chore: weekly cog update' 33 | body: Automated cog update via scheduled workflow. 34 | branch: chore/weekly-cog-update 35 | delete-branch: true 36 | reviewers: 15r10nk 37 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/912c50a461239d714485c5118fec147aadb51a0d75ec4f0aafb6d19f2afb6f72.py: -------------------------------------------------------------------------------- 1 | from ast import Assign 2 | from ast import List 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import Starred 7 | from ast import Store 8 | 9 | tree = Module( 10 | body=[ 11 | Assign( 12 | targets=[ 13 | List( 14 | elts=[ 15 | Starred(value=Name(id="name_3", ctx=Store()), ctx=Store()), 16 | Starred(value=Name(id="name_5", ctx=Store()), ctx=Store()), 17 | ], 18 | ctx=Store(), 19 | ) 20 | ], 21 | value=Name(id="name_3", ctx=Load()), 22 | ) 23 | ], 24 | type_ignores=[], 25 | ) 26 | 27 | # version: 3.12.0 28 | # 29 | # Source: 30 | # [*name_3, *name_5] = name_3 31 | # 32 | # 33 | # Error: 34 | # SyntaxError('multiple starred expressions in assignment', ('', 1, 1, None, 1, 19)) 35 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/e999b38720143cf828dc40bbedadbb00c47ad0c145e218afce81572beed828da.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Dict 3 | from ast import Expr 4 | from ast import JoinedStr 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import NamedExpr 9 | 10 | tree = Module( 11 | body=[ 12 | Expr( 13 | value=NamedExpr( 14 | target=Dict( 15 | keys=[], 16 | values=[ 17 | JoinedStr(values=[Constant(value="text")]), 18 | Constant(value=""), 19 | ], 20 | ), 21 | value=Name(id="something", ctx=Load()), 22 | ) 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # ({} := something) 32 | # 33 | # 34 | # Error: 35 | # SyntaxError('cannot use assignment expressions with dict literal', ('', 1, 2, '({} := something)\n', 1, 4)) 36 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/2eb7f1c54f705c4a7e69d9ced86a37c4df4f16a2ed625d8a9293e14f5c9c7eaf.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Match 3 | from ast import match_case 4 | from ast import MatchValue 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | from ast import UnaryOp 9 | from ast import USub 10 | 11 | tree = Module( 12 | body=[ 13 | Match( 14 | subject=Name(id="name_1", ctx=Load()), 15 | cases=[ 16 | match_case( 17 | pattern=MatchValue( 18 | value=UnaryOp(op=USub(), operand=Name(id="name_0", ctx=Load())) 19 | ), 20 | body=[Pass()], 21 | ) 22 | ], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.10.8 29 | # 30 | # Source: 31 | # match name_1: 32 | # case -name_0: 33 | # pass 34 | # 35 | # 36 | # Error: 37 | # SyntaxError('invalid syntax', ('', 2, 11, ' case -name_0:\n', 2, 17)) 38 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/ed2c2099cc5d8fe63005e937fcb54dea9556738234f4a47d366c930e26e1c927.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import MatchSequence 6 | from ast import MatchValue 7 | from ast import Module 8 | from ast import Name 9 | from ast import Pass 10 | 11 | tree = Module( 12 | body=[ 13 | Match( 14 | subject=Name(id="name_2", ctx=Load()), 15 | cases=[ 16 | match_case( 17 | pattern=MatchSequence( 18 | patterns=[MatchValue(value=Constant(value=None))] 19 | ), 20 | body=[Pass()], 21 | ) 22 | ], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # seed = 4071349 30 | # 31 | # Source: 32 | # match name_2: 33 | # case [None]: 34 | # pass 35 | # 36 | # 37 | # Error: 38 | # ValueError('unexpected constant inside of a literal pattern') 39 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d36ca6e382ec7eae65f0b5f3188a44a5f6907081e2320462cf9eddccc442bae7.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import Expr 4 | from ast import Lambda 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | 9 | tree = Module( 10 | body=[ 11 | Expr( 12 | value=Lambda( 13 | args=arguments( 14 | posonlyargs=[arg(arg="name_3", type_comment="some text")], 15 | args=[], 16 | vararg=arg(arg="name_3", type_comment=""), 17 | kwonlyargs=[], 18 | kw_defaults=[], 19 | defaults=[], 20 | ), 21 | body=Name(id="name_4", ctx=Load()), 22 | ) 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # lambda name_3, /, *name_3: name_4 32 | # 33 | # 34 | # Error: 35 | # SyntaxError("duplicate argument 'name_3' in function definition") 36 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/10a167203207604e7842f7f236a111e2bbd6f6adf4fcf54aa6d44e1aa12f2dbf.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import Expr 4 | from ast import Lambda 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | 9 | tree = Module( 10 | body=[ 11 | Expr( 12 | value=Lambda( 13 | args=arguments( 14 | posonlyargs=[arg(arg="name_5", type_comment="some text")], 15 | args=[], 16 | vararg=arg(arg="name_5", type_comment="some text"), 17 | kwonlyargs=[], 18 | kw_defaults=[], 19 | defaults=[], 20 | ), 21 | body=Name(id="name_1", ctx=Load()), 22 | ) 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # lambda name_5, /, *name_5: name_1 32 | # 33 | # 34 | # Error: 35 | # SyntaxError("duplicate argument 'name_5' in function definition") 36 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/3c8553171632b4e2697b855c004e42c6698d8c8e10e027bebfcb5153675b13ef.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import Expr 4 | from ast import Lambda 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | 9 | tree = Module( 10 | body=[ 11 | Expr( 12 | value=Lambda( 13 | args=arguments( 14 | posonlyargs=[], 15 | args=[], 16 | kwonlyargs=[arg(arg="name_2")], 17 | kw_defaults=[Name(id="name_2", ctx=Load())], 18 | kwarg=arg(arg="name_2", type_comment=""), 19 | defaults=[], 20 | ), 21 | body=Name(id="name_0", ctx=Load()), 22 | ) 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # lambda *, name_2=name_2, **name_2: name_0 32 | # 33 | # 34 | # Error: 35 | # SyntaxError("duplicate argument 'name_2' in function definition") 36 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/b579a43bd4e62c6db096e05362a80a9f7375bf2540f3afae1aeec73f00ab97b6.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import MatchValue 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | from ast import UnaryOp 10 | from ast import USub 11 | 12 | tree = Module( 13 | body=[ 14 | Match( 15 | subject=Name(id="name_3", ctx=Load()), 16 | cases=[ 17 | match_case( 18 | pattern=MatchValue( 19 | value=UnaryOp(op=USub(), operand=Constant(value=False)) 20 | ), 21 | body=[Pass()], 22 | ) 23 | ], 24 | ) 25 | ], 26 | type_ignores=[], 27 | ) 28 | 29 | # version: 3.10.8 30 | # 31 | # Source: 32 | # match name_3: 33 | # case -False: 34 | # pass 35 | # 36 | # 37 | # Error: 38 | # SyntaxError('invalid syntax', ('', 2, 11, ' case -False:\n', 2, 16)) 39 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/cb90061fd142abebb059244a45dd0581bdbed53772e47225e02db4d6c6064fda.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Global 3 | from ast import Load 4 | from ast import Match 5 | from ast import match_case 6 | from ast import MatchAs 7 | from ast import MatchValue 8 | from ast import Module 9 | from ast import Name 10 | 11 | tree = Module( 12 | body=[ 13 | Match( 14 | subject=Name(id="name_3", ctx=Load()), 15 | cases=[ 16 | match_case( 17 | pattern=MatchAs( 18 | pattern=MatchValue(value=Constant(value=0.0)), name="name_0" 19 | ), 20 | body=[Global(names=["name_0"])], 21 | ) 22 | ], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.10.8 29 | # 30 | # Source: 31 | # match name_3: 32 | # case 0.0 as name_0: 33 | # global name_0 34 | # 35 | # 36 | # Error: 37 | # SyntaxError("name 'name_0' is assigned to before global declaration") 38 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/dabf54a90b94ef885de18112fb2cffd4fa775496e4d09f93a1a4da792346e1e7.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Match 3 | from ast import match_case 4 | from ast import MatchAs 5 | from ast import MatchSingleton 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | 10 | tree = Module( 11 | body=[ 12 | Match( 13 | subject=Name(id="name_2", ctx=Load()), 14 | cases=[ 15 | match_case( 16 | pattern=MatchAs( 17 | pattern=MatchAs(pattern=MatchSingleton(value=None)), 18 | name="name_4", 19 | ), 20 | body=[Pass()], 21 | ) 22 | ], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # seed = 2121918 30 | # 31 | # Source: 32 | # match name_2: 33 | # case _ as name_4: 34 | # pass 35 | # 36 | # 37 | # Error: 38 | # ValueError('MatchAs must specify a target name if a pattern is given') 39 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/e6c6a6de2cc5595d987d42eabe19240d01a9e4e3e1405c78bea6d3f230a86467.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import FunctionDef 4 | from ast import Global 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | 10 | tree = Module( 11 | body=[ 12 | FunctionDef( 13 | name="name_3", 14 | args=arguments( 15 | posonlyargs=[], 16 | args=[arg(arg="name_4", annotation=Name(id="name_4", ctx=Load()))], 17 | kwonlyargs=[], 18 | kw_defaults=[], 19 | defaults=[], 20 | ), 21 | body=[Pass()], 22 | decorator_list=[], 23 | ), 24 | Global(names=["name_4"]), 25 | ], 26 | type_ignores=[], 27 | ) 28 | 29 | # version: 3.10.8 30 | # 31 | # Source: 32 | # def name_3(name_4: name_4): 33 | # pass 34 | # global name_4 35 | # 36 | # 37 | # Error: 38 | # SyntaxError("name 'name_4' is used prior to global declaration") 39 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/32f3dc1713079ad10291882238fdd2c7728acc1bf87c2b7fad1bd5dce4ebcf23.py: -------------------------------------------------------------------------------- 1 | from ast import AnnAssign 2 | from ast import JoinedStr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import Starred 7 | from ast import Store 8 | from ast import Subscript 9 | from ast import Tuple 10 | 11 | tree = Module( 12 | body=[ 13 | AnnAssign( 14 | target=Subscript( 15 | value=JoinedStr(values=[]), 16 | slice=Tuple( 17 | elts=[Starred(value=Name(id="name_3", ctx=Load()), ctx=Load())], 18 | ctx=Load(), 19 | ), 20 | ctx=Store(), 21 | ), 22 | annotation=Name(id="name_3", ctx=Load()), 23 | simple=2, 24 | ) 25 | ], 26 | type_ignores=[], 27 | ) 28 | 29 | # version: 3.9.15 30 | # 31 | # Source: 32 | # f''[(*name_3,)]: name_3 33 | # 34 | # 35 | # Error: 36 | # SyntaxError("can't use starred expression here", ('', 1, 6, None)) 37 | # seed = 9403548 38 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c8a7ea737ead966bbcb84c887d988813255c0a896f44c89d5bd9b2c4bbc2b7db.py: -------------------------------------------------------------------------------- 1 | from ast import Attribute 2 | from ast import Constant 3 | from ast import Load 4 | from ast import Match 5 | from ast import match_case 6 | from ast import MatchValue 7 | from ast import Module 8 | from ast import Name 9 | from ast import Pass 10 | 11 | tree = Module( 12 | body=[ 13 | Match( 14 | subject=Name(id="name_3", ctx=Load()), 15 | cases=[ 16 | match_case( 17 | pattern=MatchValue( 18 | value=Attribute( 19 | value=Constant(value=0), attr="name_1", ctx=Load() 20 | ) 21 | ), 22 | body=[Pass()], 23 | ) 24 | ], 25 | ) 26 | ] 27 | ) 28 | 29 | # version: 3.13.0b1 30 | # seed = 1710609 31 | # 32 | # Source: 33 | # match name_3: 34 | # case 0 .name_1: 35 | # pass 36 | # 37 | # 38 | # Error: 39 | # SyntaxError('invalid syntax', ('', 2, 12, ' case 0 .name_1:\n', 2, 13)) 40 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/590296c41506773455dc438ce7e9e3247d8a3ab58cf08c64bb4f04083b105313.py: -------------------------------------------------------------------------------- 1 | from ast import comprehension 2 | from ast import Expr 3 | from ast import GeneratorExp 4 | from ast import Global 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Store 9 | 10 | tree = Module( 11 | body=[ 12 | Expr( 13 | value=GeneratorExp( 14 | elt=Name(id="name_1", ctx=Load()), 15 | generators=[ 16 | comprehension( 17 | target=Name(id="name_3", ctx=Store()), 18 | iter=Name(id="name_3", ctx=Load()), 19 | ifs=[], 20 | is_async=0, 21 | ) 22 | ], 23 | ) 24 | ), 25 | Global(names=["name_3"]), 26 | ], 27 | type_ignores=[], 28 | ) 29 | 30 | # version: 3.10.8 31 | # 32 | # Source: 33 | # (name_1 for name_3 in name_3) 34 | # global name_3 35 | # 36 | # 37 | # Error: 38 | # SyntaxError("name 'name_3' is used prior to global declaration") 39 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d25777a706dfd8ace8628dc747fc1062c947bfa842940811420c36c7c98a1fd8.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import FunctionDef 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import NamedExpr 7 | from ast import Pass 8 | from ast import Store 9 | from ast import TypeVar 10 | 11 | tree = Module( 12 | body=[ 13 | FunctionDef( 14 | name="name_5", 15 | args=arguments(), 16 | body=[Pass()], 17 | type_params=[ 18 | TypeVar( 19 | name="name_4", 20 | bound=NamedExpr( 21 | target=Name(id="name_4", ctx=Store()), 22 | value=Name(id="name_5", ctx=Load()), 23 | ), 24 | ) 25 | ], 26 | ) 27 | ] 28 | ) 29 | 30 | # version: 3.13.0b1 31 | # seed = 4793203 32 | # 33 | # Source: 34 | # def name_5[name_4: (name_4 := name_5)](): 35 | # pass 36 | # 37 | # 38 | # Error: 39 | # SyntaxError('named expression cannot be used within a TypeVar bound') 40 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/2875d3f318bfc40137965386f3a131171f090ba91ab5341a30bde2dd3eef2dc0.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Match 3 | from ast import match_case 4 | from ast import MatchValue 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | 9 | tree = Module( 10 | body=[ 11 | Match( 12 | subject=Name(id="name_0", ctx=Load()), 13 | cases=[ 14 | match_case( 15 | pattern=MatchValue(value=Name(id="name_0", ctx=Load())), 16 | body=[Pass()], 17 | ), 18 | match_case( 19 | pattern=MatchValue(value=Name(id="name_0", ctx=Load())), 20 | body=[Pass()], 21 | ), 22 | ], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # match name_0: 32 | # case name_0: 33 | # pass 34 | # case name_0: 35 | # pass 36 | # 37 | # 38 | # Error: 39 | # SyntaxError("name capture 'name_0' makes remaining patterns unreachable", ('', 2, 10, None, 2, 16)) 40 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/fb5461b97acb09a942615d76f8a4368e0c6d132deb911e4f34a3821ba79dee37.py: -------------------------------------------------------------------------------- 1 | from ast import Load 2 | from ast import Match 3 | from ast import match_case 4 | from ast import MatchValue 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | 9 | tree = Module( 10 | body=[ 11 | Match( 12 | subject=Name(id="name_3", ctx=Load()), 13 | cases=[ 14 | match_case( 15 | pattern=MatchValue(value=Name(id="name_4", ctx=Load())), 16 | body=[Pass()], 17 | ), 18 | match_case( 19 | pattern=MatchValue(value=Name(id="name_1", ctx=Load())), 20 | body=[Pass()], 21 | ), 22 | ], 23 | ) 24 | ], 25 | type_ignores=[], 26 | ) 27 | 28 | # version: 3.12.0 29 | # 30 | # Source: 31 | # match name_3: 32 | # case name_4: 33 | # pass 34 | # case name_1: 35 | # pass 36 | # 37 | # 38 | # Error: 39 | # SyntaxError("name capture 'name_4' makes remaining patterns unreachable", ('', 2, 10, None, 2, 16)) 40 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/071758a387f5e3f792a7c7467d87b95daa9b285d991548b7a3c5f219fac053ce.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import BoolOp 3 | from ast import FunctionDef 4 | from ast import Global 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Or 9 | from ast import Pass 10 | 11 | tree = Module( 12 | body=[ 13 | FunctionDef( 14 | name="name_3", 15 | args=arguments( 16 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 17 | ), 18 | body=[Pass()], 19 | decorator_list=[], 20 | returns=BoolOp( 21 | op=Or(), 22 | values=[Name(id="name_0", ctx=Load()), Name(id="name_5", ctx=Load())], 23 | ), 24 | ), 25 | Global(names=["name_5"]), 26 | ], 27 | type_ignores=[], 28 | ) 29 | 30 | # version: 3.9.15 31 | # 32 | # Source: 33 | # def name_3() -> name_0 or name_5: 34 | # pass 35 | # global name_5 36 | # 37 | # 38 | # Error: 39 | # SyntaxError("name 'name_5' is used prior to global declaration") 40 | # seed = 4445204 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2022 Frank Hoffmann 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/5cfb2149b2fe84094a2b4592b31ad39bd6206d679975a36efe819ac652b063ce.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import MatchValue 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | from ast import UnaryOp 10 | from ast import USub 11 | 12 | tree = Module( 13 | body=[ 14 | Match( 15 | subject=Name(id="name_1", ctx=Load()), 16 | cases=[ 17 | match_case( 18 | pattern=MatchValue( 19 | value=UnaryOp( 20 | op=USub(), 21 | operand=UnaryOp(op=USub(), operand=Constant(value=0.0)), 22 | ) 23 | ), 24 | body=[Pass()], 25 | ) 26 | ], 27 | ) 28 | ], 29 | type_ignores=[], 30 | ) 31 | 32 | # version: 3.10.8 33 | # 34 | # Source: 35 | # match name_1: 36 | # case --0.0: 37 | # pass 38 | # 39 | # 40 | # Error: 41 | # SyntaxError('invalid syntax', ('', 2, 11, ' case --0.0:\n', 2, 12)) 42 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/3f2c13d405363a39811512559a7dc05a0d7c8d2d2b397697e3b303afa5868c6c.py: -------------------------------------------------------------------------------- 1 | from ast import Compare 2 | from ast import Expr 3 | from ast import IsNot 4 | from ast import List 5 | from ast import Load 6 | from ast import Lt 7 | from ast import Module 8 | from ast import Name 9 | from ast import NotIn 10 | from ast import Set 11 | from ast import Starred 12 | 13 | tree = Module( 14 | body=[ 15 | Expr( 16 | value=List( 17 | elts=[ 18 | Compare( 19 | left=Set(elts=[Name(id="name_1", ctx=Load())]), 20 | ops=[NotIn(), IsNot(), Lt()], 21 | comparators=[ 22 | Starred(value=Name(id="name_2", ctx=Load()), ctx=Load()) 23 | ], 24 | ) 25 | ], 26 | ctx=Load(), 27 | ) 28 | ) 29 | ], 30 | type_ignores=[], 31 | ) 32 | 33 | # version: 3.12.0 34 | # 35 | # Source: 36 | # [{name_1} not in *name_2] 37 | # 38 | # 39 | # Error: 40 | # SyntaxError('invalid syntax', ('', 1, 18, '[{name_1} not in *name_2]\n', 1, 19)) 41 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/fcb03f9b44ce1c77e3553d8433f7a9e305a57e0e166b013551bddb247931ee26.py: -------------------------------------------------------------------------------- 1 | from ast import BoolOp 2 | from ast import Load 3 | from ast import Module 4 | from ast import Name 5 | from ast import Or 6 | from ast import ParamSpec 7 | from ast import Store 8 | from ast import TypeAlias 9 | from ast import TypeVarTuple 10 | 11 | tree = Module( 12 | body=[ 13 | TypeAlias( 14 | name=Name(id="name_1", ctx=Store()), 15 | type_params=[ 16 | TypeVarTuple( 17 | name="name_4", default_value=Name(id="name_1", ctx=Load()) 18 | ), 19 | ParamSpec(name="name_1"), 20 | ], 21 | value=BoolOp( 22 | op=Or(), 23 | values=[Name(id="name_5", ctx=Load()), Name(id="name_5", ctx=Load())], 24 | ), 25 | ) 26 | ] 27 | ) 28 | 29 | # version: 3.13.0b1 30 | # seed = 4451400 31 | # 32 | # Source: 33 | # type name_1[*name_4 = name_1, **name_1] = name_5 or name_5 34 | # 35 | # 36 | # Error: 37 | # SyntaxError("non-default type parameter 'name_1' follows default type parameter", ('', 1, 31, None, 1, 39)) 38 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/1413053e9794164120c397358390f4ff9f280ceef1d623b2fad4a87e6f932c59.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import AsyncFunctionDef 4 | from ast import Global 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | 10 | tree = Module( 11 | body=[ 12 | AsyncFunctionDef( 13 | name="name_0", 14 | args=arguments( 15 | posonlyargs=[ 16 | arg(arg="name_4", annotation=Name(id="name_4", ctx=Load())) 17 | ], 18 | args=[], 19 | kwonlyargs=[], 20 | kw_defaults=[], 21 | defaults=[], 22 | ), 23 | body=[Pass()], 24 | decorator_list=[], 25 | type_params=[], 26 | ), 27 | Global(names=["name_4"]), 28 | ], 29 | type_ignores=[], 30 | ) 31 | 32 | # version: 3.12.0 33 | # seed = 8529076 34 | # 35 | # Source: 36 | # async def name_0(name_4: name_4, /): 37 | # pass 38 | # global name_4 39 | # 40 | # 41 | # Error: 42 | # SyntaxError("name 'name_4' is used prior to global declaration") 43 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/f95da16611dd6fe6c892a1be1b6a1c7b9297f66c72d7580ef81f90aaad49f8ca.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import AsyncFunctionDef 4 | from ast import Global 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | 10 | tree = Module( 11 | body=[ 12 | AsyncFunctionDef( 13 | name="name_3", 14 | args=arguments( 15 | posonlyargs=[], 16 | args=[arg(arg="name_4", annotation=Name(id="name_0", ctx=Load()))], 17 | kwonlyargs=[], 18 | kw_defaults=[ 19 | Name(id="name_5", ctx=Load()), 20 | Name(id="name_1", ctx=Load()), 21 | ], 22 | defaults=[], 23 | ), 24 | body=[Pass()], 25 | decorator_list=[], 26 | ), 27 | Global(names=["name_0"]), 28 | ], 29 | type_ignores=[], 30 | ) 31 | 32 | # version: 3.10.8 33 | # 34 | # Source: 35 | # async def name_3(name_4: name_0): 36 | # pass 37 | # global name_0 38 | # 39 | # 40 | # Error: 41 | # SyntaxError("name 'name_0' is used prior to global declaration") 42 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/ed50cc7f9a66f3f6d37790c9bbc5daf3be88c7fa1304e972ed44bb08c277c0cb.py: -------------------------------------------------------------------------------- 1 | from ast import Constant 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import MatchAs 6 | from ast import MatchValue 7 | from ast import Module 8 | from ast import Name 9 | from ast import Pass 10 | from ast import UnaryOp 11 | from ast import USub 12 | 13 | tree = Module( 14 | body=[ 15 | Match( 16 | subject=Name(id="name_4", ctx=Load()), 17 | cases=[ 18 | match_case(pattern=MatchAs(), body=[Pass()]), 19 | match_case( 20 | pattern=MatchValue( 21 | value=UnaryOp(op=USub(), operand=Constant(value=0)) 22 | ), 23 | body=[Pass()], 24 | ), 25 | ], 26 | ) 27 | ], 28 | type_ignores=[], 29 | ) 30 | 31 | # version: 3.12.0 32 | # seed = 7662891 33 | # 34 | # Source: 35 | # match name_4: 36 | # case _: 37 | # pass 38 | # case -0: 39 | # pass 40 | # 41 | # 42 | # Error: 43 | # SyntaxError('wildcard makes remaining patterns unreachable', ('', 2, 10, None, 2, 11)) 44 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/9e4e0f8cc79c918465feb8acd3d146e5aca1f14bf27eb26d99d2a57eb0e4dca9.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import Expr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import YieldFrom 8 | 9 | tree = Module( 10 | body=[ 11 | AsyncFunctionDef( 12 | name="name_0", 13 | args=arguments( 14 | posonlyargs=[], 15 | args=[], 16 | kwonlyargs=[], 17 | kw_defaults=[ 18 | Name(id="name_1", ctx=Load()), 19 | Name(id="name_4", ctx=Load()), 20 | Name(id="name_3", ctx=Load()), 21 | ], 22 | defaults=[], 23 | ), 24 | body=[Expr(value=YieldFrom(value=Name(id="name_4", ctx=Load())))], 25 | decorator_list=[], 26 | type_params=[], 27 | ) 28 | ], 29 | type_ignores=[], 30 | ) 31 | 32 | # version: 3.12.0 33 | # 34 | # Source: 35 | # async def name_0(): 36 | # yield from name_4 37 | # 38 | # 39 | # Error: 40 | # SyntaxError("'yield from' inside async function", ('', 2, 5, None, 2, 22)) 41 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/b0698ccf6761f67239761ce27f74f6800c2c9da17b913e39823a26c34e8b74a9.py: -------------------------------------------------------------------------------- 1 | from ast import BinOp 2 | from ast import Load 3 | from ast import Match 4 | from ast import match_case 5 | from ast import MatchValue 6 | from ast import Module 7 | from ast import Mult 8 | from ast import Name 9 | from ast import Pass 10 | from ast import Store 11 | 12 | tree = Module( 13 | body=[ 14 | Match( 15 | subject=Name(id="name_1", ctx=Store()), 16 | cases=[ 17 | match_case( 18 | pattern=MatchValue( 19 | value=BinOp( 20 | left=Name(id="name_4", ctx=Load()), 21 | op=Mult(), 22 | right=Name(id="name_1", ctx=Load()), 23 | ) 24 | ), 25 | body=[Pass()], 26 | ) 27 | ], 28 | ) 29 | ], 30 | type_ignores=[], 31 | ) 32 | 33 | # version: 3.10.8 34 | # 35 | # Source: 36 | # match name_1: 37 | # case name_4 * name_1: 38 | # pass 39 | # 40 | # 41 | # Error: 42 | # SyntaxError('invalid syntax', ('', 2, 17, ' case name_4 * name_1:\n', 2, 18)) 43 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d4e35786829b1c54d9492ff7a3eb6689faeaf4b9e559359f500c7062a0990e5a.py: -------------------------------------------------------------------------------- 1 | from ast import Assign 2 | from ast import ClassDef 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import Nonlocal 7 | from ast import Store 8 | 9 | tree = Module( 10 | body=[ 11 | ClassDef( 12 | name="name_1", 13 | bases=[], 14 | keywords=[], 15 | body=[ 16 | ClassDef( 17 | name="name_0", 18 | bases=[], 19 | keywords=[], 20 | body=[Nonlocal(names=["name_3"])], 21 | decorator_list=[], 22 | ), 23 | Assign( 24 | targets=[Name(id="name_3", ctx=Store())], 25 | value=Name(id="name_1", ctx=Load()), 26 | ), 27 | ], 28 | decorator_list=[], 29 | ) 30 | ], 31 | type_ignores=[], 32 | ) 33 | 34 | # version: 3.10.8 35 | # 36 | # Source: 37 | # class name_1: 38 | # 39 | # class name_0: 40 | # nonlocal name_3 41 | # name_3 = name_1 42 | # 43 | # 44 | # Error: 45 | # SyntaxError("no binding for nonlocal 'name_3' found") 46 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/13f0a847a3e6c83ed61e8e4caf058d1bdafa60352c0279c7b0dfd2e412b66224.py: -------------------------------------------------------------------------------- 1 | from ast import Compare 2 | from ast import comprehension 3 | from ast import Expr 4 | from ast import ListComp 5 | from ast import Load 6 | from ast import LtE 7 | from ast import Module 8 | from ast import Name 9 | 10 | tree = Module( 11 | body=[ 12 | Expr( 13 | value=ListComp( 14 | elt=Name(id="name_4", ctx=Load()), 15 | generators=[ 16 | comprehension( 17 | target=Compare( 18 | left=Name(id="name_1", ctx=Load()), 19 | ops=[LtE()], 20 | comparators=[Name(id="name_2", ctx=Load())], 21 | ), 22 | iter=Name(id="name_5", ctx=Load()), 23 | ifs=[], 24 | is_async=0, 25 | ) 26 | ], 27 | ) 28 | ) 29 | ], 30 | type_ignores=[], 31 | ) 32 | 33 | # version: 3.12.0 34 | # 35 | # Source: 36 | # [name_4 for name_1 <= name_2 in name_5] 37 | # 38 | # 39 | # Error: 40 | # SyntaxError('invalid syntax', ('', 1, 20, '[name_4 for name_1 <= name_2 in name_5]\n', 1, 22)) 41 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/ee06f0cef6ed0c54cf617760a109a7d2a796b8513960153038a9c8383fc97ed3.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import FunctionDef 4 | from ast import Global 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Pass 9 | 10 | tree = Module( 11 | body=[ 12 | FunctionDef( 13 | name="name_0", 14 | args=arguments( 15 | posonlyargs=[], 16 | args=[ 17 | arg( 18 | arg="name_3", 19 | annotation=Name(id="name_5", ctx=Load()), 20 | type_comment="some text", 21 | ) 22 | ], 23 | kwonlyargs=[], 24 | kw_defaults=[], 25 | defaults=[], 26 | ), 27 | body=[Pass()], 28 | decorator_list=[], 29 | type_params=[], 30 | ), 31 | Global(names=["name_5"]), 32 | ], 33 | type_ignores=[], 34 | ) 35 | 36 | # version: 3.12.0 37 | # seed = 774070 38 | # 39 | # Source: 40 | # def name_0(name_3: name_5): 41 | # pass 42 | # global name_5 43 | # 44 | # 45 | # Error: 46 | # SyntaxError("name 'name_5' is used prior to global declaration") 47 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/1db51410f27fe100fcfc10212cb4070d8c54bf15f399df430c1192f61a710cd1.py: -------------------------------------------------------------------------------- 1 | from ast import Break 2 | from ast import ExceptHandler 3 | from ast import For 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | from ast import Store 9 | from ast import TryStar 10 | 11 | tree = Module( 12 | body=[ 13 | For( 14 | target=Name(id="name_1", ctx=Store()), 15 | iter=Name(id="something", ctx=Load()), 16 | body=[ 17 | TryStar( 18 | body=[Pass()], 19 | handlers=[ 20 | ExceptHandler( 21 | type=Name(id="name_4", ctx=Load()), body=[Break()] 22 | ) 23 | ], 24 | orelse=[], 25 | finalbody=[], 26 | ) 27 | ], 28 | orelse=[], 29 | ) 30 | ], 31 | type_ignores=[], 32 | ) 33 | 34 | # version: 3.12.0 35 | # 36 | # Source: 37 | # for name_1 in something: 38 | # try: 39 | # pass 40 | # except* name_4: 41 | # break 42 | # 43 | # 44 | # Error: 45 | # SyntaxError("'break', 'continue' and 'return' cannot appear in an except* block", ('', 5, 9, None, 5, 14)) 46 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/4c1e530d2c379853a4b1c0917c53e69bb39a842e83b77576e3627842d6354672.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import Break 4 | from ast import For 5 | from ast import Module 6 | from ast import Name 7 | from ast import Store 8 | 9 | tree = Module( 10 | body=[ 11 | For( 12 | target=Name(id="name_4", ctx=Store()), 13 | iter=Name(id="name_5", ctx=Store()), 14 | body=[ 15 | AsyncFunctionDef( 16 | name="name_5", 17 | args=arguments( 18 | posonlyargs=[], 19 | args=[], 20 | kwonlyargs=[], 21 | kw_defaults=[], 22 | defaults=[], 23 | ), 24 | body=[Break()], 25 | decorator_list=[], 26 | type_params=[], 27 | ) 28 | ], 29 | orelse=[], 30 | ) 31 | ], 32 | type_ignores=[], 33 | ) 34 | 35 | # version: 3.12.0 36 | # 37 | # Source: 38 | # for name_4 in name_5: 39 | # 40 | # async def name_5(): 41 | # break 42 | # 43 | # 44 | # Error: 45 | # SyntaxError("'break' outside loop", ('', 4, 9, None, 4, 14)) 46 | -------------------------------------------------------------------------------- /find_new_issue.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import threading 4 | from concurrent.futures import as_completed 5 | from concurrent.futures import ThreadPoolExecutor 6 | from random import randint 7 | 8 | from tests.test_invalid_ast import generate_invalid_ast 9 | 10 | if __name__ == "__main__": 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument("--seed", type=int, help="Test only one seed value") 13 | parser.add_argument( 14 | "--workers", type=int, default=os.cpu_count(), help="Number of parallel workers" 15 | ) 16 | args = parser.parse_args() 17 | 18 | if args.seed is not None: 19 | print(f"Testing seed {args.seed}") 20 | generate_invalid_ast(args.seed) 21 | else: 22 | found = threading.Event() 23 | 24 | def try_seed(): 25 | while not found.is_set(): 26 | i = randint(0, 10000000000) 27 | if generate_invalid_ast(i): 28 | found.set() 29 | print(f"Found seed: {i}") 30 | return i 31 | 32 | with ThreadPoolExecutor(max_workers=args.workers) as executor: 33 | futures = [executor.submit(try_seed) for _ in range(args.workers)] 34 | for future in as_completed(futures): 35 | if future.result() is not None: 36 | break 37 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/6aec1c1ffde5c4d0ff20ad461e532db4bfbb892a95b89fd306d9d3605f6eacf9.py: -------------------------------------------------------------------------------- 1 | from ast import Attribute 2 | from ast import Constant 3 | from ast import Load 4 | from ast import Match 5 | from ast import match_case 6 | from ast import MatchValue 7 | from ast import Module 8 | from ast import Name 9 | from ast import Pass 10 | from ast import UnaryOp 11 | from ast import USub 12 | 13 | tree = Module( 14 | body=[ 15 | Match( 16 | subject=Name(id="name_1", ctx=Load()), 17 | cases=[ 18 | match_case( 19 | pattern=MatchValue( 20 | value=UnaryOp( 21 | op=USub(), 22 | operand=Attribute( 23 | value=Constant(value="", kind=""), 24 | attr="name_0", 25 | ctx=Load(), 26 | ), 27 | ) 28 | ), 29 | body=[Pass()], 30 | ) 31 | ], 32 | ) 33 | ], 34 | type_ignores=[], 35 | ) 36 | 37 | # version: 3.10.8 38 | # 39 | # Source: 40 | # match name_1: 41 | # case -''.name_0: 42 | # pass 43 | # 44 | # 45 | # Error: 46 | # SyntaxError('invalid syntax', ('', 2, 11, " case -''.name_0:\n", 2, 13)) 47 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/544887cb7d6d057d5d1962ac235882167a5a5a4439a234f2f50bfd9c1a4dfd86.py: -------------------------------------------------------------------------------- 1 | from ast import BoolOp 2 | from ast import Expr 3 | from ast import Load 4 | from ast import Module 5 | from ast import Name 6 | from ast import Or 7 | from ast import Slice 8 | from ast import Starred 9 | from ast import Store 10 | from ast import Subscript 11 | from ast import Tuple 12 | 13 | tree = Module( 14 | body=[ 15 | Expr( 16 | value=Subscript( 17 | value=BoolOp( 18 | op=Or(), 19 | values=[ 20 | Name(id="name_2", ctx=Load()), 21 | Name(id="name_0", ctx=Load()), 22 | ], 23 | ), 24 | slice=Tuple( 25 | elts=[ 26 | Slice(upper=Name(id="name_3", ctx=Load())), 27 | Starred(value=Name(id="name_4", ctx=Load()), ctx=Load()), 28 | ], 29 | ctx=Load(), 30 | ), 31 | ctx=Store(), 32 | ) 33 | ) 34 | ], 35 | type_ignores=[], 36 | ) 37 | 38 | # version: 3.10.8 39 | # 40 | # Source: 41 | # (name_2 or name_0)[(:name_3, *name_4)] 42 | # 43 | # 44 | # Error: 45 | # SyntaxError('invalid syntax', ('', 1, 21, '(name_2 or name_0)[(:name_3, *name_4)]\n', 1, 22)) 46 | -------------------------------------------------------------------------------- /tests/TestBase.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import unittest 3 | import warnings 4 | 5 | from pysource_codegen._codegen import unparse 6 | 7 | 8 | class TestBase(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.details = [] 12 | super().setUp() 13 | 14 | def addDetail(self, *text): 15 | text = " ".join(map(str, text)) 16 | if hasattr(self, "details"): 17 | self.details.append(text) 18 | 19 | def message(self): 20 | return "detailed info:\n" + "\n\n".join(self.details) 21 | 22 | def does_compile(self, tree: ast.Module): 23 | for node in ast.walk(tree): 24 | if isinstance(node, ast.BoolOp) and len(node.values) < 2: 25 | return False 26 | if not isinstance(node, ast.JoinedStr) and any( 27 | isinstance(n, ast.FormattedValue) for n in ast.iter_child_nodes(node) 28 | ): 29 | return False 30 | try: 31 | with warnings.catch_warnings(): 32 | warnings.simplefilter("ignore", SyntaxWarning) 33 | source = unparse(tree) 34 | compile(source, "", "exec") 35 | compile(ast.fix_missing_locations(tree), "", "exec") 36 | except Exception as e: 37 | self.addDetail("exception during `compile(ast.unparse(tree))`:\n" + str(e)) 38 | return False 39 | return True 40 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | from multiprocessing import get_context 4 | 5 | from .test_invalid_ast import generate_invalid_ast 6 | 7 | 8 | def pytest_addoption(parser, pluginmanager): 9 | parser.addoption( 10 | "--generate-samples", 11 | action="store_true", 12 | help="Config file to use, defaults to %(default)s", 13 | ) 14 | 15 | 16 | def generate(seed): 17 | return generate_invalid_ast(seed) # or generate_valid_source(seed) 18 | 19 | 20 | def seeds(): 21 | return [random.randint(0, 10000000) for _ in range(10000)] 22 | 23 | 24 | def pytest_sessionfinish(session, exitstatus): 25 | print("exitstatus", exitstatus) 26 | 27 | if exitstatus == 0 and session.config.option.generate_samples: 28 | end_time = time.time() + 60 * 5 29 | if False: 30 | for seed in seeds(): 31 | if generate(seed): 32 | break 33 | if time.time() > end_time: 34 | print("Timeout") 35 | break 36 | else: 37 | with get_context("spawn").Pool(maxtasksperchild=100) as p: 38 | for r in p.imap_unordered(generate, seeds()): 39 | if r: 40 | break 41 | 42 | if time.time() > end_time: 43 | print("Timeout") 44 | break 45 | p.terminate() 46 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/ad9221c70ea548d130ca84aed1bfe1eaae37693b14a654272842d6c886e6a920.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import ClassDef 3 | from ast import Lambda 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import Store 8 | from ast import TypeAlias 9 | 10 | tree = Module( 11 | body=[ 12 | ClassDef( 13 | name="name_4", 14 | bases=[], 15 | keywords=[], 16 | body=[ 17 | TypeAlias( 18 | name=Name(id="name_4", ctx=Store()), 19 | type_params=[], 20 | value=Lambda( 21 | args=arguments( 22 | posonlyargs=[], 23 | args=[], 24 | kwonlyargs=[], 25 | kw_defaults=[], 26 | defaults=[], 27 | ), 28 | body=Name(id="name_3", ctx=Load()), 29 | ), 30 | ) 31 | ], 32 | decorator_list=[], 33 | type_params=[], 34 | ) 35 | ], 36 | type_ignores=[], 37 | ) 38 | 39 | # version: 3.12.0 40 | # seed = 6421668 41 | # 42 | # Source: 43 | # class name_4: 44 | # type name_4 = lambda: name_3 45 | # 46 | # 47 | # Error: 48 | # SyntaxError('Cannot use lambda in annotation scope within class scope') 49 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/d065d738a49528171cadadb911340474ebb7cb25b74653fa6f76383b5b39c267.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import comprehension 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import SetComp 8 | from ast import Store 9 | from ast import TypeAlias 10 | 11 | tree = Module( 12 | body=[ 13 | AsyncFunctionDef( 14 | name="name_0", 15 | args=arguments(), 16 | body=[ 17 | TypeAlias( 18 | name=Name(id="name_0", ctx=Store()), 19 | value=SetComp( 20 | elt=Name(id="name_3", ctx=Load()), 21 | generators=[ 22 | comprehension( 23 | target=Name(id="name_3", ctx=Store()), 24 | iter=Name(id="name_2", ctx=Load()), 25 | is_async=3, 26 | ) 27 | ], 28 | ), 29 | ) 30 | ], 31 | ) 32 | ] 33 | ) 34 | 35 | # version: 3.13.0b1 36 | # seed = 6246222 37 | # 38 | # Source: 39 | # async def name_0(): 40 | # type name_0 = {name_3 async for name_3 in name_2} 41 | # 42 | # 43 | # Error: 44 | # SyntaxError('asynchronous comprehension outside of an asynchronous function', ('', 2, 19, None, 2, 54)) 45 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/3ee08d92317acdfb57bcd1a2fe9a6e9c7dad7aa337ed1ca879ff72922c94ff2a.py: -------------------------------------------------------------------------------- 1 | from ast import Attribute 2 | from ast import Constant 3 | from ast import Dict 4 | from ast import JoinedStr 5 | from ast import List 6 | from ast import Load 7 | from ast import Module 8 | from ast import Name 9 | from ast import Set 10 | from ast import Starred 11 | from ast import TypeAlias 12 | 13 | tree = Module( 14 | body=[ 15 | TypeAlias( 16 | name=None, 17 | type_params=[], 18 | value=Dict( 19 | keys=[ 20 | Set( 21 | elts=[ 22 | List(elts=[Name(id="name_5", ctx=Load())], ctx=Load()), 23 | Starred(value=Name(id="name_1", ctx=Load()), ctx=Load()), 24 | List(elts=[Name(id="name_3", ctx=Load())], ctx=Load()), 25 | JoinedStr(values=[Constant(value="text")]), 26 | ] 27 | ), 28 | Attribute( 29 | value=Set(elts=[Name(id="name_5", ctx=Load())]), 30 | attr="name_5", 31 | ctx=Load(), 32 | ), 33 | ], 34 | values=[], 35 | ), 36 | ) 37 | ], 38 | type_ignores=[], 39 | ) 40 | 41 | # version: 3.12.0 42 | # 43 | # 44 | # Error: 45 | # AttributeError("'NoneType' object has no attribute '_fields'") 46 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/ecdff53cb488c4b0a95d1194691f4ed13a81ed47ae1d5525a18ec045c3740a88.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import ClassDef 4 | from ast import FunctionDef 5 | from ast import Global 6 | from ast import Module 7 | from ast import Nonlocal 8 | 9 | tree = Module( 10 | body=[ 11 | FunctionDef( 12 | name="name_5", 13 | args=arguments( 14 | posonlyargs=[], 15 | args=[], 16 | vararg=None, 17 | kwonlyargs=[ 18 | arg(arg="name_4", annotation=None, type_comment="some text") 19 | ], 20 | kw_defaults=[None], 21 | kwarg=None, 22 | defaults=[], 23 | ), 24 | body=[ 25 | ClassDef( 26 | name="name_5", 27 | bases=[], 28 | keywords=[], 29 | body=[Global(names=["name_4"]), Nonlocal(names=["name_4"])], 30 | decorator_list=[], 31 | ) 32 | ], 33 | decorator_list=[], 34 | returns=None, 35 | type_comment=None, 36 | ) 37 | ], 38 | type_ignores=[], 39 | ) 40 | 41 | # version: 3.8.16 42 | # seed = 6589600 43 | # 44 | # Source: 45 | # 46 | # 47 | # def name_5(*, name_4): 48 | # 49 | # class name_5(): 50 | # global name_4 51 | # nonlocal name_4 52 | # 53 | # 54 | # 55 | # Error: 56 | # SyntaxError("name 'name_4' is nonlocal and global") 57 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/aec9d24bbe842109d37324991ad4e729fd4cda3434180bd85ef5e5e6b741da77.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import FunctionDef 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | from ast import Return 9 | from ast import Yield 10 | 11 | tree = Module( 12 | body=[ 13 | AsyncFunctionDef( 14 | name="name_2", 15 | args=arguments( 16 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 17 | ), 18 | body=[ 19 | FunctionDef( 20 | name="name_4", 21 | args=arguments( 22 | posonlyargs=[], 23 | args=[], 24 | kwonlyargs=[], 25 | kw_defaults=[], 26 | defaults=[], 27 | ), 28 | body=[Pass()], 29 | decorator_list=[], 30 | returns=Yield(), 31 | ), 32 | Return(value=Name(id="name_0", ctx=Load())), 33 | ], 34 | decorator_list=[], 35 | ) 36 | ], 37 | type_ignores=[], 38 | ) 39 | 40 | # version: 3.9.15 41 | # seed = 4392892 42 | # 43 | # Source: 44 | # async def name_2(): 45 | # 46 | # def name_4() -> (yield): 47 | # pass 48 | # return name_0 49 | # 50 | # 51 | # Error: 52 | # SyntaxError("'return' with value in async generator", ('', 5, 5, None)) 53 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/0bff03cb20bbb5235e7a1f4224a855bea7203bd4a872777f8aacf947f2aa9163.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import Continue 3 | from ast import FunctionDef 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import While 8 | 9 | tree = Module( 10 | body=[ 11 | While( 12 | test=Name(id="name_0", ctx=Load()), 13 | body=[ 14 | FunctionDef( 15 | name="name_4", 16 | args=arguments( 17 | posonlyargs=[], 18 | args=[], 19 | kwonlyargs=[], 20 | kw_defaults=[ 21 | Name(id="name_2", ctx=Load()), 22 | Name(id="name_1", ctx=Load()), 23 | Name(id="name_0", ctx=Load()), 24 | Name(id="name_0", ctx=Load()), 25 | Name(id="name_2", ctx=Load()), 26 | ], 27 | defaults=[], 28 | ), 29 | body=[Continue()], 30 | decorator_list=[], 31 | type_params=[], 32 | ) 33 | ], 34 | orelse=[], 35 | ) 36 | ], 37 | type_ignores=[], 38 | ) 39 | 40 | # version: 3.12.0 41 | # 42 | # Source: 43 | # while name_0: 44 | # 45 | # def name_4(): 46 | # continue 47 | # 48 | # 49 | # Error: 50 | # SyntaxError("'continue' not properly in loop", ('', 4, 9, None, 4, 17)) 51 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/a741bd1c5bfa9a9f7a630b1ef5b00ddd5e5f952c11c99946827560a7ff739318.py: -------------------------------------------------------------------------------- 1 | from ast import Attribute 2 | from ast import Constant 3 | from ast import Load 4 | from ast import Match 5 | from ast import match_case 6 | from ast import MatchAs 7 | from ast import MatchOr 8 | from ast import MatchValue 9 | from ast import Module 10 | from ast import Name 11 | from ast import Pass 12 | 13 | tree = Module( 14 | body=[ 15 | Match( 16 | subject=Name(id="name_4", ctx=Load()), 17 | cases=[ 18 | match_case( 19 | pattern=MatchOr( 20 | patterns=[ 21 | MatchValue(value=Constant(value=b"")), 22 | MatchAs( 23 | pattern=MatchValue( 24 | value=Attribute( 25 | value=Name(id="name_5", ctx=Load()), 26 | attr="name_5", 27 | ctx=Load(), 28 | ) 29 | ) 30 | ), 31 | ] 32 | ), 33 | body=[Pass()], 34 | ) 35 | ], 36 | ) 37 | ], 38 | type_ignores=[], 39 | ) 40 | 41 | # version: 3.12.0 42 | # seed = 3572769 43 | # 44 | # Source: 45 | # match name_4: 46 | # case b'' | _: 47 | # pass 48 | # 49 | # 50 | # Error: 51 | # ValueError('MatchAs must specify a target name if a pattern is given') 52 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/bf4dd4d22a1f2bb85fa42bbb3c18ba8fc6c5115b7f5a0c36a0c7474d131c1a9a.py: -------------------------------------------------------------------------------- 1 | from ast import Await 2 | from ast import BoolOp 3 | from ast import comprehension 4 | from ast import Dict 5 | from ast import Expr 6 | from ast import GeneratorExp 7 | from ast import Load 8 | from ast import Module 9 | from ast import Name 10 | from ast import Or 11 | from ast import Store 12 | from ast import Subscript 13 | from ast import Tuple 14 | 15 | tree = Module( 16 | body=[ 17 | Expr( 18 | value=GeneratorExp( 19 | elt=Dict(), 20 | generators=[ 21 | comprehension( 22 | target=Subscript( 23 | value=Name(id="name_1", ctx=Load()), 24 | slice=BoolOp( 25 | op=Or(), 26 | values=[ 27 | Name(id="name_2", ctx=Load()), 28 | Name(id="name_0", ctx=Load()), 29 | ], 30 | ), 31 | ctx=Store(), 32 | ), 33 | iter=Await(value=Tuple(ctx=Load())), 34 | is_async=0, 35 | ) 36 | ], 37 | ) 38 | ) 39 | ] 40 | ) 41 | 42 | # version: 3.13.0b1 43 | # seed = 3002626 44 | # 45 | # Source: 46 | # ({} for name_1[name_2 or name_0] in await ()) 47 | # 48 | # 49 | # Error: 50 | # SyntaxError("'await' outside function", ('', 1, 37, None, 1, 45)) 51 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/72dc7899219471ef8a847f9ca2129cba5f63f9c81a9e79293491c727bfbe2867.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import comprehension 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | from ast import SetComp 9 | from ast import Store 10 | from ast import TypeVar 11 | 12 | tree = Module( 13 | body=[ 14 | AsyncFunctionDef( 15 | name="name_3", 16 | args=arguments(), 17 | body=[ 18 | AsyncFunctionDef( 19 | name="name_0", 20 | args=arguments(), 21 | body=[Pass()], 22 | returns=SetComp( 23 | elt=Name(id="name_0", ctx=Load()), 24 | generators=[ 25 | comprehension( 26 | target=Name(id="name_4", ctx=Store()), 27 | iter=Name(id="name_3", ctx=Load()), 28 | is_async=3, 29 | ) 30 | ], 31 | ), 32 | type_params=[TypeVar(name="name_5")], 33 | ) 34 | ], 35 | ) 36 | ] 37 | ) 38 | 39 | # version: 3.13.0b1 40 | # seed = 1238331 41 | # 42 | # Source: 43 | # async def name_3(): 44 | # 45 | # async def name_0[name_5]() -> {name_0 async for name_4 in name_3}: 46 | # pass 47 | # 48 | # 49 | # Error: 50 | # SyntaxError('asynchronous comprehension outside of an asynchronous function', ('', 3, 35, None, 3, 70)) 51 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/e9f4db361a1f95b628548012eb4fcd30d9b5df81722e245e47f4c23f4f25c2c7.py: -------------------------------------------------------------------------------- 1 | from ast import Assign 2 | from ast import comprehension 3 | from ast import List 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import SetComp 8 | from ast import Starred 9 | from ast import Store 10 | 11 | tree = Module( 12 | body=[ 13 | Assign( 14 | targets=[ 15 | List( 16 | elts=[ 17 | Starred( 18 | value=SetComp( 19 | elt=Name(id="name_0", ctx=Load()), 20 | generators=[ 21 | comprehension( 22 | target=Name(id="name_5", ctx=Store()), 23 | iter=Name(id="name_5", ctx=Load()), 24 | ifs=[], 25 | is_async=0, 26 | ) 27 | ], 28 | ), 29 | ctx=Store(), 30 | ) 31 | ], 32 | ctx=Store(), 33 | ) 34 | ], 35 | value=Name(id="name_0", ctx=Load()), 36 | ) 37 | ], 38 | type_ignores=[], 39 | ) 40 | 41 | # version: 3.12.0 42 | # 43 | # Source: 44 | # [*{name_0 for name_5 in name_5}] = name_0 45 | # 46 | # 47 | # Error: 48 | # SyntaxError('cannot assign to set comprehension', ('', 1, 3, '[*{name_0 for name_5 in name_5}] = name_0\n', 1, 32)) 49 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/2b3d3db96b4b46a0f97cb5c664a614584cbc7f7439db4d8cd17d42820c741635.py: -------------------------------------------------------------------------------- 1 | from ast import ClassDef 2 | from ast import comprehension 3 | from ast import Expr 4 | from ast import ListComp 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Nonlocal 9 | from ast import Store 10 | 11 | tree = Module( 12 | body=[ 13 | ClassDef( 14 | name="name_3", 15 | bases=[], 16 | keywords=[], 17 | body=[ 18 | Expr( 19 | value=ListComp( 20 | elt=Name(id="name_2", ctx=Load()), 21 | generators=[ 22 | comprehension( 23 | target=Name(id="name_1", ctx=Store()), 24 | iter=Name(id="name_1", ctx=Load()), 25 | ifs=[], 26 | is_async=0, 27 | ) 28 | ], 29 | ) 30 | ), 31 | ClassDef( 32 | name="name_5", 33 | bases=[], 34 | keywords=[], 35 | body=[Nonlocal(names=["name_1"])], 36 | decorator_list=[], 37 | ), 38 | ], 39 | decorator_list=[], 40 | ) 41 | ], 42 | type_ignores=[], 43 | ) 44 | 45 | # version: 3.10.8 46 | # 47 | # Source: 48 | # class name_3: 49 | # [name_2 for name_1 in name_1] 50 | # 51 | # class name_5: 52 | # nonlocal name_1 53 | # 54 | # 55 | # Error: 56 | # SyntaxError("no binding for nonlocal 'name_1' found") 57 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - hooks: 3 | - id: check-yaml 4 | # - id: check-ast 5 | - id: check-docstring-first 6 | - id: check-merge-conflict 7 | - id: trailing-whitespace 8 | - id: mixed-line-ending 9 | - id: fix-byte-order-marker 10 | - id: check-case-conflict 11 | - id: check-json 12 | - id: end-of-file-fixer 13 | repo: https://github.com/pre-commit/pre-commit-hooks 14 | rev: v4.6.0 15 | - hooks: 16 | - args: 17 | - --in-place 18 | - --expand-star-imports 19 | - --remove-all-unused-imports 20 | - --ignore-init-module-imports 21 | id: autoflake 22 | repo: https://github.com/myint/autoflake 23 | rev: v2.3.1 24 | 25 | - repo: https://github.com/asottile/setup-cfg-fmt 26 | rev: v2.5.0 27 | hooks: 28 | - id: setup-cfg-fmt 29 | 30 | - repo: https://github.com/asottile/reorder-python-imports 31 | rev: v3.13.0 32 | hooks: 33 | - args: 34 | - --py38-plus 35 | id: reorder-python-imports 36 | - hooks: 37 | - args: 38 | - --py38-plus 39 | id: pyupgrade 40 | repo: https://github.com/asottile/pyupgrade 41 | rev: v3.15.2 42 | - hooks: 43 | - id: black 44 | repo: https://github.com/psf/black 45 | rev: 24.4.2 46 | - hooks: 47 | - id: blacken-docs 48 | repo: https://github.com/asottile/blacken-docs 49 | rev: 1.16.0 50 | 51 | - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks 52 | rev: v2.13.0 53 | hooks: 54 | - id: pretty-format-yaml 55 | args: [--autofix, --indent, '2'] 56 | 57 | - hooks: 58 | - id: blackdoc 59 | repo: https://github.com/keewis/blackdoc 60 | rev: v0.3.9 61 | - hooks: 62 | - id: commitizen 63 | stages: 64 | - commit-msg 65 | repo: https://github.com/commitizen-tools/commitizen 66 | rev: v3.27.0 67 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/fc379af3330f835cf6d80f06c7a90625dad0b23588e17ca8b7d64ec7004a2b0e.py: -------------------------------------------------------------------------------- 1 | from ast import AnnAssign 2 | from ast import arg 3 | from ast import arguments 4 | from ast import ClassDef 5 | from ast import FunctionDef 6 | from ast import Load 7 | from ast import Module 8 | from ast import Name 9 | from ast import Nonlocal 10 | from ast import Store 11 | 12 | tree = Module( 13 | body=[ 14 | FunctionDef( 15 | name="name_3", 16 | args=arguments( 17 | posonlyargs=[], 18 | args=[], 19 | kwonlyargs=[arg(arg="name_3")], 20 | kw_defaults=[None], 21 | defaults=[], 22 | ), 23 | body=[ 24 | ClassDef( 25 | name="name_4", 26 | bases=[], 27 | keywords=[], 28 | body=[ 29 | Nonlocal(names=["name_3"]), 30 | AnnAssign( 31 | target=Name(id="name_3", ctx=Store()), 32 | annotation=Name(id="name_5", ctx=Load()), 33 | value=Name(id="name_1", ctx=Load()), 34 | simple=2, 35 | ), 36 | ], 37 | decorator_list=[], 38 | ) 39 | ], 40 | decorator_list=[], 41 | ) 42 | ], 43 | type_ignores=[], 44 | ) 45 | 46 | # version: 3.9.15 47 | # 48 | # Source: 49 | # def name_3(*, name_3): 50 | # 51 | # class name_4: 52 | # nonlocal name_3 53 | # name_3: name_5 = name_1 54 | # 55 | # 56 | # Error: 57 | # SyntaxError("annotated name 'name_3' can't be nonlocal") 58 | # seed = 5322869 59 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/4e4e76d27980319b522279cca8d9f44a49f276635b08ac999eb17a24e9037b83.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import comprehension 4 | from ast import Expr 5 | from ast import Global 6 | from ast import ListComp 7 | from ast import Load 8 | from ast import Module 9 | from ast import Name 10 | from ast import NamedExpr 11 | from ast import Store 12 | 13 | tree = Module( 14 | body=[ 15 | AsyncFunctionDef( 16 | name="name_5", 17 | args=arguments(), 18 | body=[ 19 | Expr( 20 | value=ListComp( 21 | elt=Name(id="name_3", ctx=Load()), 22 | generators=[ 23 | comprehension( 24 | target=Name(id="name_3", ctx=Store()), 25 | iter=Name(id="name_5", ctx=Load()), 26 | ifs=[ 27 | NamedExpr( 28 | target=Name(id="name_1", ctx=Store()), 29 | value=Name(id="name_3", ctx=Load()), 30 | ) 31 | ], 32 | is_async=0, 33 | ) 34 | ], 35 | ) 36 | ), 37 | Global(names=["name_1"]), 38 | ], 39 | ) 40 | ] 41 | ) 42 | 43 | # version: 3.13.0b2+ 44 | # seed = 587 45 | # 46 | # Source: 47 | # async def name_5(): 48 | # [name_3 for name_3 in name_5 if (name_1 := name_3)] 49 | # global name_1 50 | # 51 | # 52 | # Error: 53 | # SyntaxError("name 'name_1' is assigned to before global declaration") 54 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/c6deaccaac47a6e3c5aeb86d03df0ba8aafeef0035da0875935835a8d92979c1.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import comprehension 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | from ast import Pass 8 | from ast import SetComp 9 | from ast import Store 10 | from ast import TypeVar 11 | 12 | tree = Module( 13 | body=[ 14 | AsyncFunctionDef( 15 | name="name_1", 16 | args=arguments(), 17 | body=[ 18 | AsyncFunctionDef( 19 | name="name_4", 20 | args=arguments(), 21 | body=[Pass()], 22 | type_params=[ 23 | TypeVar( 24 | name="name_1", 25 | bound=SetComp( 26 | elt=Name(id="name_4", ctx=Load()), 27 | generators=[ 28 | comprehension( 29 | target=Name(id="name_1", ctx=Store()), 30 | iter=Name(id="name_5", ctx=Load()), 31 | is_async=4, 32 | ) 33 | ], 34 | ), 35 | ) 36 | ], 37 | ) 38 | ], 39 | ) 40 | ] 41 | ) 42 | 43 | # version: 3.13.0b1 44 | # seed = 2128177 45 | # 46 | # Source: 47 | # async def name_1(): 48 | # 49 | # async def name_4[name_1: {name_4 async for name_1 in name_5}](): 50 | # pass 51 | # 52 | # 53 | # Error: 54 | # SyntaxError('asynchronous comprehension outside of an asynchronous function', ('', 3, 30, None, 3, 65)) 55 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/22d7e90d8e6830c2b773a687048fee622d52f9a483748d228f446062b110c9cc.py: -------------------------------------------------------------------------------- 1 | from ast import ClassDef 2 | from ast import comprehension 3 | from ast import GeneratorExp 4 | from ast import keyword 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Nonlocal 9 | from ast import Store 10 | 11 | tree = Module( 12 | body=[ 13 | ClassDef( 14 | name="name_4", 15 | bases=[], 16 | keywords=[], 17 | body=[ 18 | ClassDef( 19 | name="name_0", 20 | bases=[], 21 | keywords=[ 22 | keyword( 23 | value=GeneratorExp( 24 | elt=Name(id="name_5", ctx=Load()), 25 | generators=[ 26 | comprehension( 27 | target=Name(id="name_2", ctx=Store()), 28 | iter=Name(id="name_2", ctx=Load()), 29 | ifs=[], 30 | is_async=1, 31 | ) 32 | ], 33 | ) 34 | ) 35 | ], 36 | body=[Nonlocal(names=["name_2"])], 37 | decorator_list=[], 38 | ) 39 | ], 40 | decorator_list=[], 41 | ) 42 | ], 43 | type_ignores=[], 44 | ) 45 | 46 | # version: 3.10.8 47 | # 48 | # Source: 49 | # class name_4: 50 | # 51 | # class name_0(**(name_5 async for name_2 in name_2)): 52 | # nonlocal name_2 53 | # 54 | # 55 | # Error: 56 | # SyntaxError("no binding for nonlocal 'name_2' found") 57 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/536d989999e06d3d084d8dc3f3b5f9534f80edaa792a12d6982555135497d870.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import ClassDef 3 | from ast import comprehension 4 | from ast import FunctionDef 5 | from ast import Load 6 | from ast import Module 7 | from ast import Name 8 | from ast import Nonlocal 9 | from ast import SetComp 10 | from ast import Store 11 | 12 | tree = Module( 13 | body=[ 14 | FunctionDef( 15 | name="name_4", 16 | args=arguments( 17 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 18 | ), 19 | body=[ 20 | ClassDef( 21 | name="name_1", 22 | bases=[ 23 | SetComp( 24 | elt=Name(id="name_3", ctx=Load()), 25 | generators=[ 26 | comprehension( 27 | target=Name(id="name_0", ctx=Store()), 28 | iter=Name(id="name_5", ctx=Load()), 29 | ifs=[], 30 | is_async=0, 31 | ) 32 | ], 33 | ) 34 | ], 35 | keywords=[], 36 | body=[Nonlocal(names=["name_0"])], 37 | decorator_list=[], 38 | ) 39 | ], 40 | decorator_list=[], 41 | ) 42 | ], 43 | type_ignores=[], 44 | ) 45 | 46 | # version: 3.9.15 47 | # seed = 4500971 48 | # 49 | # Source: 50 | # def name_4(): 51 | # 52 | # class name_1({name_3 for name_0 in name_5}): 53 | # nonlocal name_0 54 | # 55 | # 56 | # Error: 57 | # SyntaxError("no binding for nonlocal 'name_0' found") 58 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/7a47b8156beea57004fd29228b00571faeef3226f75049521bb76c53fd0cd31c.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import AsyncFunctionDef 4 | from ast import comprehension 5 | from ast import ListComp 6 | from ast import Load 7 | from ast import Module 8 | from ast import Name 9 | from ast import Pass 10 | from ast import Store 11 | from ast import TypeVar 12 | 13 | tree = Module( 14 | body=[ 15 | AsyncFunctionDef( 16 | name="name_2", 17 | args=arguments(), 18 | body=[ 19 | AsyncFunctionDef( 20 | name="name_5", 21 | args=arguments( 22 | kwonlyargs=[ 23 | arg( 24 | arg="name_1", 25 | annotation=ListComp( 26 | elt=Name(id="name_4", ctx=Load()), 27 | generators=[ 28 | comprehension( 29 | target=Name(id="name_3", ctx=Store()), 30 | iter=Name(id="name_4", ctx=Load()), 31 | is_async=3, 32 | ) 33 | ], 34 | ), 35 | ) 36 | ], 37 | kw_defaults=[None], 38 | ), 39 | body=[Pass()], 40 | type_params=[TypeVar(name="name_3")], 41 | ) 42 | ], 43 | ) 44 | ] 45 | ) 46 | 47 | # version: 3.13.0b1 48 | # seed = 6570464 49 | # 50 | # Source: 51 | # async def name_2(): 52 | # 53 | # async def name_5[name_3](*, name_1: [name_4 async for name_3 in name_4]): 54 | # pass 55 | # 56 | # 57 | # Error: 58 | # SyntaxError('asynchronous comprehension outside of an asynchronous function', ('', 3, 41, None, 3, 76)) 59 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/b6b6c0d5ded78f9f06861c44ebd58dde9bdf0d4ce708d8344d48b164321196a8.py: -------------------------------------------------------------------------------- 1 | from ast import * 2 | 3 | tree = Module( 4 | body=[ 5 | Delete( 6 | targets=[ 7 | Subscript( 8 | value=Name(id="name_2", ctx=Load()), 9 | slice=Index( 10 | value=Subscript( 11 | value=Name(id="name_0", ctx=Load()), 12 | slice=ExtSlice( 13 | dims=[ 14 | ExtSlice( 15 | dims=[ 16 | ExtSlice( 17 | dims=[ 18 | ExtSlice( 19 | dims=[ 20 | Index( 21 | value=Name( 22 | id="name_2", 23 | ctx=Load(), 24 | ) 25 | ) 26 | ] 27 | ) 28 | ] 29 | ) 30 | ] 31 | ) 32 | ] 33 | ), 34 | ctx=Load(), 35 | ) 36 | ), 37 | ctx=Del(), 38 | ) 39 | ] 40 | ) 41 | ], 42 | type_ignores=[], 43 | ) 44 | 45 | # version: 3.8.16 46 | # seed = 8040275 47 | # 48 | # Source: 49 | # 50 | # del name_2[name_0[name_2]] 51 | # 52 | # 53 | # 54 | # Error: 55 | # SystemError('extended slice invalid in nested slice') 56 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/e030558ac8d9ce0385686711b5b3cc6f0fec95103400f786ad8bfdf5327b0880.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import comprehension 3 | from ast import DictComp 4 | from ast import FunctionDef 5 | from ast import Global 6 | from ast import Load 7 | from ast import Match 8 | from ast import match_case 9 | from ast import MatchSingleton 10 | from ast import Module 11 | from ast import Name 12 | from ast import NamedExpr 13 | from ast import Store 14 | 15 | tree = Module( 16 | body=[ 17 | FunctionDef( 18 | name="name_0", 19 | args=arguments( 20 | posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] 21 | ), 22 | body=[ 23 | Match( 24 | subject=DictComp( 25 | key=Name(id="name_0", ctx=Load()), 26 | value=NamedExpr( 27 | target=Name(id="name_3", ctx=Load()), 28 | value=Name(id="name_3", ctx=Load()), 29 | ), 30 | generators=[ 31 | comprehension( 32 | target=Name(id="name_4", ctx=Store()), 33 | iter=Name(id="name_1", ctx=Load()), 34 | ifs=[], 35 | is_async=0, 36 | ) 37 | ], 38 | ), 39 | cases=[ 40 | match_case( 41 | pattern=MatchSingleton(value=19), 42 | body=[Global(names=["name_3"])], 43 | ) 44 | ], 45 | ) 46 | ], 47 | decorator_list=[], 48 | ) 49 | ], 50 | type_ignores=[], 51 | ) 52 | 53 | # version: 3.11.0 54 | # seed = 8805753 55 | # 56 | # Source: 57 | # def name_0(): 58 | # match {name_0: (name_3 := name_3) for name_4 in name_1}: 59 | # case 19: 60 | # global name_3 61 | # 62 | # 63 | # Error: 64 | # SyntaxError("name 'name_3' is assigned to before global declaration") 65 | -------------------------------------------------------------------------------- /pysource_codegen/ast_info.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import ast 4 | import inspect 5 | import re 6 | import sys 7 | 8 | from .types import BuiltinNodeType 9 | from .types import NodeType 10 | from .types import UnionNodeType 11 | 12 | 13 | type_infos: dict[str, NodeType | BuiltinNodeType | UnionNodeType] = {} 14 | 15 | 16 | def get_info(name): 17 | if name in type_infos: 18 | return type_infos[name] 19 | elif name in ("identifier", "int", "string", "constant"): 20 | type_infos[name] = BuiltinNodeType(name) 21 | 22 | else: 23 | doc = inspect.getdoc(getattr(ast, name)) or "" 24 | doc = doc.replace("\n", " ") 25 | 26 | if doc: 27 | m = re.fullmatch(r"(\w*)", doc) 28 | if m: 29 | nt = NodeType(fields={}, ast_type=getattr(ast, name)) 30 | name = m.group(1) 31 | type_infos[name] = nt 32 | else: 33 | m = re.fullmatch(r"(\w*)\((.*)\)", doc) 34 | if m: 35 | nt = NodeType(fields={}, ast_type=getattr(ast, name)) 36 | name = m.group(1) 37 | type_infos[name] = nt 38 | for string_field in m.group(2).split(","): 39 | field_type, field_name = string_field.split() 40 | quantity = "" 41 | while (last := field_type[-1]) in "*?": 42 | quantity = last + quantity 43 | field_type = field_type[:-1] 44 | 45 | nt.fields[field_name] = (field_type, quantity) 46 | get_info(field_type) 47 | elif doc.startswith(f"{name} = "): 48 | doc = doc.split(" = ", 1)[1] 49 | nt = UnionNodeType(options=[]) 50 | type_infos[name] = nt 51 | nt.options = [d.split("(")[0] for d in doc.split(" | ")] 52 | for o in nt.options: 53 | get_info(o) 54 | 55 | else: 56 | assert False, "can not parse:" + doc 57 | else: 58 | assert False, "no doc for " + name 59 | 60 | return type_infos[name] 61 | 62 | 63 | if sys.version_info < (3, 9): 64 | from .static_type_info import type_infos # type: ignore 65 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/9051062853b2853e1ee23e859e808ed7e9cbce367fdd703c694db7bf631b3b63.py: -------------------------------------------------------------------------------- 1 | from ast import Expr 2 | from ast import FormattedValue 3 | from ast import JoinedStr 4 | from ast import Load 5 | from ast import Module 6 | from ast import Name 7 | 8 | tree = Module( 9 | body=[ 10 | Expr( 11 | value=JoinedStr( 12 | values=[ 13 | FormattedValue( 14 | value=Name(id="name_0", ctx=Load()), 15 | conversion=-1, 16 | format_spec=JoinedStr( 17 | values=[ 18 | FormattedValue( 19 | value=Name(id="name_1", ctx=Load()), 20 | conversion=115, 21 | format_spec=JoinedStr( 22 | values=[ 23 | FormattedValue( 24 | value=Name(id="name_1", ctx=Load()), 25 | conversion=-1, 26 | format_spec=JoinedStr( 27 | values=[ 28 | FormattedValue( 29 | value=Name( 30 | id="name_5", ctx=Load() 31 | ), 32 | conversion=97, 33 | ) 34 | ] 35 | ), 36 | ) 37 | ] 38 | ), 39 | ) 40 | ] 41 | ), 42 | ) 43 | ] 44 | ) 45 | ) 46 | ], 47 | type_ignores=[], 48 | ) 49 | 50 | # version: 3.12.0 51 | # 52 | # Source: 53 | # f'{name_0:{name_1!s:{name_1:{name_5!a}}}}' 54 | # 55 | # 56 | # Error: 57 | # SyntaxError('f-string: expressions nested too deeply', ('', 1, 28, "f'{name_0:{name_1!s:{name_1:{name_5!a}}}}'", 1, 28)) 58 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/a005252def05828fe471eec5daef145d3332a2935b0c317f596ce6118e017682.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import Compare 3 | from ast import FunctionDef 4 | from ast import Gt 5 | from ast import Is 6 | from ast import Load 7 | from ast import Lt 8 | from ast import LtE 9 | from ast import Module 10 | from ast import Name 11 | from ast import NamedExpr 12 | from ast import Pass 13 | from ast import Tuple 14 | from ast import TypeVar 15 | 16 | tree = Module( 17 | body=[ 18 | FunctionDef( 19 | name="name_0", 20 | args=arguments( 21 | posonlyargs=[], 22 | args=[], 23 | kwonlyargs=[], 24 | kw_defaults=[ 25 | Tuple( 26 | elts=[ 27 | Name(id="name_2", ctx=Load()), 28 | Name(id="name_4", ctx=Load()), 29 | Name(id="name_5", ctx=Load()), 30 | Name(id="name_3", ctx=Load()), 31 | Name(id="name_3", ctx=Load()), 32 | Name(id="name_2", ctx=Load()), 33 | ], 34 | ctx=Load(), 35 | ), 36 | Compare( 37 | left=Name(id="name_0", ctx=Load()), 38 | ops=[Gt(), LtE(), Lt(), Gt(), Is()], 39 | comparators=[ 40 | Name(id="name_3", ctx=Load()), 41 | Name(id="name_0", ctx=Load()), 42 | Name(id="name_4", ctx=Load()), 43 | ], 44 | ), 45 | ], 46 | defaults=[], 47 | ), 48 | body=[Pass()], 49 | decorator_list=[], 50 | type_params=[ 51 | TypeVar( 52 | name="name_2", 53 | bound=NamedExpr( 54 | target=Name(id="name_3", ctx=Load()), 55 | value=Name(id="name_3", ctx=Load()), 56 | ), 57 | ) 58 | ], 59 | ) 60 | ], 61 | type_ignores=[], 62 | ) 63 | 64 | # version: 3.12.0 65 | # 66 | # Source: 67 | # def name_0[name_2: (name_3 := name_3)](): 68 | # pass 69 | # 70 | # 71 | # Error: 72 | # SyntaxError('named expression cannot be used within a TypeVar bound') 73 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/a9331f90fa3c39247dfa99a8412c105e66cfa7a61b3c253728225042098676d6.py: -------------------------------------------------------------------------------- 1 | from ast import comprehension 2 | from ast import Constant 3 | from ast import Dict 4 | from ast import DictComp 5 | from ast import Expr 6 | from ast import JoinedStr 7 | from ast import Load 8 | from ast import Module 9 | from ast import Name 10 | from ast import NamedExpr 11 | from ast import SetComp 12 | from ast import Store 13 | from ast import Subscript 14 | 15 | tree = Module( 16 | body=[ 17 | Expr( 18 | value=DictComp( 19 | key=Name(id="name_1", ctx=Load()), 20 | value=Dict( 21 | keys=[ 22 | NamedExpr( 23 | target=Name(id="name_4", ctx=Load()), 24 | value=Name(id="name_2", ctx=Load()), 25 | ), 26 | JoinedStr(values=[Constant(value="text")]), 27 | SetComp( 28 | elt=Name(id="name_3", ctx=Load()), 29 | generators=[ 30 | comprehension( 31 | target=Name(id="name_1", ctx=Store()), 32 | iter=Name(id="name_5", ctx=Load()), 33 | ifs=[Name(id="name_1", ctx=Load())], 34 | is_async=0, 35 | ) 36 | ], 37 | ), 38 | Subscript( 39 | value=Name(id="name_4", ctx=Load()), 40 | slice=Name(id="name_4", ctx=Load()), 41 | ctx=Load(), 42 | ), 43 | ], 44 | values=[], 45 | ), 46 | generators=[ 47 | comprehension( 48 | target=Constant(value="some const text"), 49 | iter=Name(id="name_0", ctx=Load()), 50 | ifs=[], 51 | is_async=0, 52 | ) 53 | ], 54 | ) 55 | ) 56 | ], 57 | type_ignores=[], 58 | ) 59 | 60 | # version: 3.12.0 61 | # 62 | # Source: 63 | # {name_1: {} for 'some const text' in name_0} 64 | # 65 | # 66 | # Error: 67 | # SyntaxError('cannot assign to literal', ('', 1, 17, "{name_1: {} for 'some const text' in name_0}\n", 1, 34)) 68 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main] 7 | 8 | jobs: 9 | mypy: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-python@v4 17 | with: 18 | python-version: ${{matrix.python-version}} 19 | architecture: x64 20 | allow-prereleases: true 21 | - run: pip install hatch 22 | - run: hatch run +py=${{matrix.python-version}} types:check 23 | 24 | test: 25 | runs-on: ubuntu-latest 26 | strategy: 27 | matrix: 28 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] 29 | steps: 30 | - uses: actions/checkout@v3 31 | - uses: actions/setup-python@v4 32 | with: 33 | python-version: ${{matrix.python-version}} 34 | architecture: x64 35 | allow-prereleases: true 36 | - run: pip install hatch 37 | - run: hatch test -py ${{matrix.python-version}} 38 | 39 | 40 | publish: 41 | name: Publish new release 42 | runs-on: ubuntu-latest 43 | needs: [test, mypy] 44 | environment: pypi 45 | permissions: 46 | # IMPORTANT: this permission is mandatory for Trusted Publishing 47 | id-token: write 48 | # this permission is mandatory to create github releases 49 | contents: write 50 | 51 | steps: 52 | - name: Checkout main 53 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 54 | with: 55 | fetch-depth: 0 56 | 57 | - name: Install uv 58 | uses: astral-sh/setup-uv@v5 59 | with: 60 | python-version: '3.12' 61 | 62 | - name: Check if the commit has a vx.y.z tag 63 | id: check-version 64 | run: | 65 | if git tag --list --points-at ${{ github.sha }} | grep -q -E '^v[0-9]+\.[0-9]+\.[0-9]+$'; then 66 | echo "is new version" 67 | echo "should_continue=true" >> "$GITHUB_OUTPUT" 68 | else 69 | echo "is not a new version" 70 | echo "should_continue=false" >> "$GITHUB_OUTPUT" 71 | fi 72 | 73 | - run: uv pip install hatch scriv 74 | 75 | - name: build package 76 | run: hatch build 77 | 78 | - name: Publish package distributions to PyPI 79 | if: ${{ steps.check-version.outputs.should_continue == 'true' }} 80 | uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 81 | 82 | - name: create github release 83 | if: ${{ steps.check-version.outputs.should_continue == 'true' }} 84 | env: 85 | GITHUB_TOKEN: ${{ github.token }} 86 | run: scriv github-release 87 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/aefa14a91815738f75c920c0d2d2ecf8725c0ccbee291ef24b6f3d0a9b78dbb2.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import Attribute 4 | from ast import BoolOp 5 | from ast import ExceptHandler 6 | from ast import FunctionDef 7 | from ast import Load 8 | from ast import Module 9 | from ast import Name 10 | from ast import Or 11 | from ast import Pass 12 | from ast import Return 13 | from ast import Try 14 | from ast import Yield 15 | 16 | tree = Module( 17 | body=[ 18 | AsyncFunctionDef( 19 | name="name_3", 20 | args=arguments( 21 | posonlyargs=[], 22 | args=[], 23 | kwonlyargs=[], 24 | kw_defaults=[ 25 | Attribute( 26 | value=BoolOp( 27 | op=Or(), 28 | values=[ 29 | Name(id="name_1", ctx=Load()), 30 | Name(id="name_4", ctx=Load()), 31 | Name(id="name_3", ctx=Load()), 32 | Name(id="name_2", ctx=Load()), 33 | ], 34 | ), 35 | attr="name_3", 36 | ctx=Load(), 37 | ) 38 | ], 39 | defaults=[], 40 | ), 41 | body=[ 42 | Try( 43 | body=[Return(value=Name(id="name_5", ctx=Load()))], 44 | handlers=[ExceptHandler(body=[Pass()])], 45 | orelse=[], 46 | finalbody=[ 47 | FunctionDef( 48 | name="name_3", 49 | args=arguments( 50 | posonlyargs=[], 51 | args=[], 52 | kwonlyargs=[], 53 | kw_defaults=[ 54 | Name(id="name_1", ctx=Load()), 55 | Name(id="name_3", ctx=Load()), 56 | Name(id="name_0", ctx=Load()), 57 | Name(id="name_0", ctx=Load()), 58 | Name(id="name_3", ctx=Load()), 59 | ], 60 | defaults=[], 61 | ), 62 | body=[Pass()], 63 | decorator_list=[Yield()], 64 | ) 65 | ], 66 | ) 67 | ], 68 | decorator_list=[], 69 | ) 70 | ], 71 | type_ignores=[], 72 | ) 73 | 74 | # version: 3.10.8 75 | # 76 | # Source: 77 | # async def name_3(): 78 | # try: 79 | # return name_5 80 | # except: 81 | # pass 82 | # finally: 83 | # 84 | # @(yield) 85 | # def name_3(): 86 | # pass 87 | # 88 | # 89 | # Error: 90 | # SyntaxError("'return' with value in async generator", ('', 3, 9, None, 3, 22)) 91 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "pysource-codegen" 7 | version= "0.7.1" 8 | description = 'generate random python code to test linter/formatter/and other tools' 9 | readme = "README.md" 10 | requires-python = ">=3.8" 11 | license = "MIT" 12 | keywords = [] 13 | authors = [ 14 | { name = "Frank Hoffmann", email = "15r10nk-git@polarbit.de" }, 15 | ] 16 | classifiers = [ 17 | "Development Status :: 4 - Beta", 18 | "Programming Language :: Python", 19 | "Programming Language :: Python :: 3.8", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | "Programming Language :: Python :: 3.13", 25 | "Programming Language :: Python :: 3.14", 26 | "Programming Language :: Python :: Implementation :: CPython", 27 | "Programming Language :: Python :: Implementation :: PyPy", 28 | "License :: OSI Approved :: MIT License", 29 | ] 30 | dependencies = [ 31 | "astunparse >=1.6.3; python_version<'3.9' ", 32 | "typing-extensions>=4.7.1" 33 | ] 34 | 35 | [project.urls] 36 | Documentation = "https://github.com/15r10nk/pysource-codegen#readme" 37 | Issues = "https://github.com/15r10nk/pysource-codegen/issues" 38 | Source = "https://github.com/15r10nk/pysource-codegen" 39 | 40 | [tool.hatch.version] 41 | path = "pysource_codegen/__init__.py" 42 | 43 | [[tool.hatch.envs.hatch-test.matrix]] 44 | python=["3.8","3.9","3.10","3.11","3.12","3.13","3.14"] 45 | 46 | [tool.hatch.envs.hatch-test] 47 | extra-dependencies=[ 48 | "rich>=12.0.0", 49 | "inline-snapshot>=0.4.0", 50 | "pysource-minimize>=0.5.0" 51 | ] 52 | 53 | [[tool.hatch.envs.types.matrix]] 54 | python=["3.8","3.9","3.10","3.11","3.12","3.13","3.14"] 55 | 56 | [tool.hatch.envs.types] 57 | extra-dependencies = [ 58 | "mypy>=1.0.0", 59 | "rich>=12.0.0", 60 | "inline-snapshot>=0.4.0", 61 | "pysource-minimize>=0.5.0", 62 | "pytest" 63 | ] 64 | 65 | [tool.hatch.envs.types.scripts] 66 | check = "mypy --install-types --non-interactive {args:pysource_codegen tests}" 67 | 68 | [tool.hatch.envs.cog] 69 | dependencies=["cogapp","lxml","requests"] 70 | scripts.update="cog -r *.md" 71 | 72 | [tool.coverage.run] 73 | source_pkgs = ["pysource_codegen", "tests"] 74 | branch = true 75 | parallel = true 76 | omit = [ 77 | "pysource_codegen/__about__.py", 78 | ] 79 | 80 | [tool.coverage.paths] 81 | pysource_codegen = ["pysource_codegen", "*/pysource-codegen/pysource_codegen"] 82 | tests = ["tests", "*/pysource-codegen/tests"] 83 | 84 | [tool.coverage.report] 85 | exclude_lines = ["assert False", "raise NotImplemented"] 86 | 87 | [tool.commitizen] 88 | changelog_incremental = true 89 | major_version_zero = true 90 | tag_format = "v$major.$minor.$patch$prerelease" 91 | update_changelog_on_bump = true 92 | version_files = [ 93 | "pysource_codegen/__init__.py:__version__" 94 | ] 95 | version_provider = "pep621" 96 | 97 | [tool.scriv] 98 | format = "md" 99 | version = "command: cz bump --get-next" 100 | 101 | [tool.mypy] 102 | exclude="tests/.*_samples" 103 | [tool.hatch.envs.release.scripts] 104 | create=[ 105 | "cz bump", 106 | "git push --force-with-lease origin main $(git describe main --tags)", 107 | ] 108 | 109 | [project.scripts] 110 | pysource-codegen = "pysource_codegen.__main__:run" 111 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/70bbc09cf8a083e05fd35d0b20f3cab4b9aca486d136097f3fe7cbebd6f07bae.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import ClassDef 4 | from ast import Compare 5 | from ast import comprehension 6 | from ast import DictComp 7 | from ast import FunctionDef 8 | from ast import In 9 | from ast import Lambda 10 | from ast import Load 11 | from ast import Module 12 | from ast import Name 13 | from ast import Nonlocal 14 | from ast import Store 15 | from ast import Subscript 16 | 17 | tree = Module( 18 | body=[ 19 | FunctionDef( 20 | name="name_3", 21 | args=arguments( 22 | posonlyargs=[], 23 | args=[], 24 | kwonlyargs=[], 25 | kw_defaults=[], 26 | defaults=[ 27 | Subscript( 28 | value=Name(id="name_1", ctx=Load()), 29 | slice=Name(id="name_1", ctx=Load()), 30 | ctx=Load(), 31 | ), 32 | Compare( 33 | left=Name(id="name_5", ctx=Load()), 34 | ops=[In(), In(), In()], 35 | comparators=[ 36 | Name(id="name_1", ctx=Load()), 37 | Name(id="name_2", ctx=Load()), 38 | Name(id="name_4", ctx=Load()), 39 | Name(id="name_2", ctx=Load()), 40 | Name(id="name_1", ctx=Load()), 41 | Name(id="name_5", ctx=Load()), 42 | ], 43 | ), 44 | Lambda( 45 | args=arguments( 46 | posonlyargs=[arg(arg="name_2", type_comment="some text")], 47 | args=[arg(arg="name_1", type_comment="")], 48 | kwonlyargs=[arg(arg="name_4", type_comment="")], 49 | kw_defaults=[Name(id="name_3", ctx=Load())], 50 | defaults=[Name(id="name_5", ctx=Load())], 51 | ), 52 | body=Name(id="name_1", ctx=Load()), 53 | ), 54 | ], 55 | ), 56 | body=[ 57 | ClassDef( 58 | name="name_3", 59 | bases=[], 60 | keywords=[], 61 | body=[Nonlocal(names=["name_4"])], 62 | decorator_list=[ 63 | DictComp( 64 | key=Name(id="name_2", ctx=Load()), 65 | value=Name(id="name_5", ctx=Load()), 66 | generators=[ 67 | comprehension( 68 | target=Name(id="name_4", ctx=Store()), 69 | iter=Name(id="name_3", ctx=Load()), 70 | ifs=[], 71 | is_async=0, 72 | ) 73 | ], 74 | ) 75 | ], 76 | ) 77 | ], 78 | decorator_list=[], 79 | ) 80 | ], 81 | type_ignores=[], 82 | ) 83 | 84 | # version: 3.9.15 85 | # 86 | # Source: 87 | # def name_3(): 88 | # 89 | # @{name_2: name_5 for name_4 in name_3} 90 | # class name_3: 91 | # nonlocal name_4 92 | # 93 | # 94 | # Error: 95 | # SyntaxError("no binding for nonlocal 'name_4' found") 96 | # seed = 7904827 97 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/0ef0cad747ad103e36fc87402a82b452cdc2846882c1795705672e226b8781cd.py: -------------------------------------------------------------------------------- 1 | from ast import And 2 | from ast import arg 3 | from ast import arguments 4 | from ast import AsyncFor 5 | from ast import AsyncFunctionDef 6 | from ast import BinOp 7 | from ast import BoolOp 8 | from ast import FloorDiv 9 | from ast import FunctionDef 10 | from ast import Lambda 11 | from ast import Load 12 | from ast import Module 13 | from ast import Name 14 | from ast import Pass 15 | from ast import Store 16 | 17 | tree = Module( 18 | body=[ 19 | AsyncFunctionDef( 20 | name="name_4", 21 | args=arguments( 22 | posonlyargs=[], 23 | args=[], 24 | kwonlyargs=[], 25 | kw_defaults=[ 26 | BoolOp( 27 | op=And(), 28 | values=[ 29 | Name(id="name_5", ctx=Load()), 30 | Name(id="name_3", ctx=Load()), 31 | Name(id="name_1", ctx=Load()), 32 | ], 33 | ), 34 | BinOp( 35 | left=Name(id="name_5", ctx=Load()), 36 | op=FloorDiv(), 37 | right=Name(id="name_4", ctx=Load()), 38 | ), 39 | BoolOp( 40 | op=And(), 41 | values=[ 42 | Name(id="name_3", ctx=Load()), 43 | Name(id="name_3", ctx=Load()), 44 | Name(id="name_2", ctx=Load()), 45 | ], 46 | ), 47 | Lambda( 48 | args=arguments( 49 | posonlyargs=[arg(arg="name_5")], 50 | args=[arg(arg="name_3", type_comment="some text")], 51 | vararg=arg(arg="name_2"), 52 | kwonlyargs=[arg(arg="name_4", type_comment="")], 53 | kw_defaults=[Name(id="name_1", ctx=Load())], 54 | kwarg=arg(arg="name_0", type_comment="some text"), 55 | defaults=[Name(id="name_4", ctx=Load())], 56 | ), 57 | body=Name(id="name_2", ctx=Load()), 58 | ), 59 | ], 60 | defaults=[], 61 | ), 62 | body=[ 63 | FunctionDef( 64 | name="name_1", 65 | args=arguments( 66 | posonlyargs=[], 67 | args=[], 68 | kwonlyargs=[], 69 | kw_defaults=[Name(id="name_0", ctx=Load())], 70 | defaults=[], 71 | ), 72 | body=[ 73 | AsyncFor( 74 | target=Name(id="name_0", ctx=Store()), 75 | iter=Name(id="name_4", ctx=Load()), 76 | body=[Pass()], 77 | orelse=[], 78 | type_comment="some text", 79 | ) 80 | ], 81 | decorator_list=[], 82 | type_params=[], 83 | ) 84 | ], 85 | decorator_list=[], 86 | type_params=[], 87 | ) 88 | ], 89 | type_ignores=[], 90 | ) 91 | 92 | # version: 3.12.0 93 | # 94 | # Source: 95 | # async def name_4(): 96 | # 97 | # def name_1(): 98 | # async for name_0 in name_4: # type: some text 99 | # pass 100 | # 101 | # 102 | # Error: 103 | # SyntaxError("'async for' outside async function", ('', 4, 9, None, 5, 17)) 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![pypi version](https://img.shields.io/pypi/v/pysource-codegen.svg)](https://pypi.org/project/pysource-codegen/) 2 | ![Python Versions](https://img.shields.io/pypi/pyversions/pysource-codegen) 3 | ![PyPI - Downloads](https://img.shields.io/pypi/dw/pysource-codegen) 4 | [![GitHub Sponsors](https://img.shields.io/github/sponsors/15r10nk)](https://github.com/sponsors/15r10nk) 5 | 6 | # Introduction 7 | 8 | 9 | `pysource_codegen` is able to generate random python code which can be compiled. 10 | The compiled code should not be executed. 11 | 12 | This is still a very early version, but it does its job. 13 | It is general useful to test tools like formatters, linters or anything which operates on python code. 14 | 15 | ## Install: 16 | ``` bash 17 | pip install pysource-codegen 18 | ``` 19 | 20 | ## Usage: 21 | 22 | The tool can be used over the CLI: 23 | 24 | ``` bash 25 | pysource-codegen --seed 42 26 | ``` 27 | 28 | or as a library: 29 | 30 | ``` python 31 | from pysource_codegen import generate 32 | 33 | seed = 42 34 | print(generate(seed)) 35 | ``` 36 | 37 | You might find [pysource-minimize](https://github.com/15r10nk/pysource-minimize) also useful 38 | to reduce the generated code which triggers your bug down to a minimal code snipped, 39 | which can be used to fix the issue. 40 | 41 | ``` python 42 | from pysource_codegen import generate 43 | from pysource_minimize import minimize 44 | 45 | 46 | def contains_bug(code): 47 | """ 48 | returns True if the code triggers a bug and False otherwise 49 | """ 50 | try: 51 | test_something_with(code) # might throw 52 | 53 | if "bug" in code: # maybe other checks 54 | return True 55 | except: 56 | return True 57 | return False 58 | 59 | 60 | def find_issue(): 61 | for seed in range(0, 10000): 62 | code = generate(seed) 63 | 64 | if contains_bug(code): 65 | new_code = minimize(code, contains_bug) 66 | 67 | print("the following code triggers a bug") 68 | print(new_code) 69 | 70 | return 71 | 72 | 73 | find_issue() 74 | ``` 75 | 76 | 77 | ## Bugs found in other projects: 78 | 79 | ### black 80 | 81 | * https://github.com/psf/black/issues/3676 82 | * https://github.com/psf/black/issues/3678 83 | * https://github.com/psf/black/issues/3677 84 | 85 | ### cpython 86 | 87 | #### 3.12 88 | * https://github.com/python/cpython/issues/109219 89 | * https://github.com/python/cpython/issues/109823 90 | * https://github.com/python/cpython/issues/109719 91 | * https://github.com/python/cpython/issues/109627 92 | * https://github.com/python/cpython/issues/109219 93 | * https://github.com/python/cpython/issues/109118 94 | * https://github.com/python/cpython/issues/109114 95 | * https://github.com/python/cpython/issues/109889 96 | 97 | #### 3.13 98 | * https://github.com/python/cpython/issues/120367 99 | * https://github.com/python/cpython/issues/120225 100 | * https://github.com/python/cpython/issues/124746 101 | * https://github.com/python/cpython/issues/124871 102 | 103 | #### 3.14 104 | 105 | * https://github.com/python/cpython/issues/138558 106 | * https://github.com/python/cpython/issues/138479 107 | * https://github.com/python/cpython/issues/138349 108 | * https://github.com/python/cpython/issues/132479 109 | 110 | 118 | ## Sponsors 119 | 120 | I would like to thank my sponsors. Without them, I would not be able to invest so much time in my projects. 121 | 122 | ### Silver sponsor 🥈 123 | 124 |

125 | 126 | logfire 127 | 128 |

129 | 130 | 131 | 132 | ## Todo: 133 | 134 | * [ ] refactor the existing code 135 | * add better context information to `probability()` 136 | * remove `fix()` function and move code into `probability()` 137 | * [ ] use probabilities for the ast-nodes from existing python code (use markov chains) 138 | * [x] support older python versions 139 | * [ ] allow to customize the probabilities to generate code to test specific language features 140 | * [ ] [hypothesis](https://hypothesis.readthedocs.io/en/latest/) support 141 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/8499312d9638cfdaf5040898d911d9642959e830c4acef3c9bdb89cf676b6d5c.py: -------------------------------------------------------------------------------- 1 | from ast import arg 2 | from ast import arguments 3 | from ast import Attribute 4 | from ast import comprehension 5 | from ast import Constant 6 | from ast import FunctionDef 7 | from ast import GeneratorExp 8 | from ast import JoinedStr 9 | from ast import Lambda 10 | from ast import Load 11 | from ast import Module 12 | from ast import Name 13 | from ast import Pass 14 | from ast import Set 15 | from ast import Store 16 | 17 | tree = Module( 18 | body=[ 19 | FunctionDef( 20 | name="name_4", 21 | args=arguments( 22 | posonlyargs=[], 23 | args=[], 24 | kwonlyargs=[ 25 | arg( 26 | arg="name_2", 27 | annotation=GeneratorExp( 28 | elt=Lambda( 29 | args=arguments( 30 | posonlyargs=[arg(arg="name_5")], 31 | args=[arg(arg="name_3", type_comment="some text")], 32 | kwonlyargs=[arg(arg="name_1")], 33 | kw_defaults=[Name(id="name_1", ctx=Load())], 34 | defaults=[Name(id="name_5", ctx=Load())], 35 | ), 36 | body=Name(id="name_0", ctx=Load()), 37 | ), 38 | generators=[ 39 | comprehension( 40 | target=Name(id="name_3", ctx=Store()), 41 | iter=Name(id="name_0", ctx=Load()), 42 | ifs=[ 43 | Name(id="name_0", ctx=Load()), 44 | Name(id="name_2", ctx=Load()), 45 | Name(id="name_4", ctx=Load()), 46 | Name(id="name_0", ctx=Load()), 47 | Name(id="name_3", ctx=Load()), 48 | ], 49 | is_async=4, 50 | ), 51 | comprehension( 52 | target=Name(id="name_0", ctx=Store()), 53 | iter=Name(id="name_5", ctx=Load()), 54 | ifs=[ 55 | Name(id="name_0", ctx=Load()), 56 | Name(id="name_5", ctx=Load()), 57 | ], 58 | is_async=4, 59 | ), 60 | comprehension( 61 | target=Name(id="name_2", ctx=Store()), 62 | iter=Name(id="name_2", ctx=Load()), 63 | ifs=[Name(id="name_2", ctx=Load())], 64 | is_async=1, 65 | ), 66 | comprehension( 67 | target=Name(id="name_1", ctx=Store()), 68 | iter=Name(id="name_5", ctx=Load()), 69 | ifs=[ 70 | Name(id="name_3", ctx=Load()), 71 | Name(id="name_4", ctx=Load()), 72 | Name(id="name_2", ctx=Load()), 73 | Name(id="name_2", ctx=Load()), 74 | Name(id="name_1", ctx=Load()), 75 | ], 76 | is_async=0, 77 | ), 78 | ], 79 | ), 80 | type_comment="", 81 | ) 82 | ], 83 | kw_defaults=[], 84 | defaults=[ 85 | JoinedStr(values=[Constant(value="")]), 86 | Attribute( 87 | value=Set(elts=[Name(id="name_1", ctx=Load())]), 88 | attr="name_2", 89 | ctx=Load(), 90 | ), 91 | ], 92 | ), 93 | body=[Pass()], 94 | decorator_list=[], 95 | ) 96 | ], 97 | type_ignores=[], 98 | ) 99 | 100 | # version: 3.9.15 101 | # 102 | # Source: 103 | # def name_4(*): 104 | # pass 105 | # 106 | # 107 | # Error: 108 | # SyntaxError('named arguments must follow bare *', ('', 1, 13, 'def name_4(*):\n')) 109 | -------------------------------------------------------------------------------- /tests/test_valid_source.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import hashlib 3 | import sys 4 | from pathlib import Path 5 | from unittest.mock import patch 6 | 7 | sys.path.append(str(Path(__file__).parent.parent.parent / "pysource-minimize" / "src")) 8 | 9 | from pysource_codegen._codegen import generate_ast 10 | from pysource_codegen._codegen import is_valid_ast 11 | from pysource_codegen._codegen import unparse 12 | from pysource_minimize import minimize 13 | 14 | from .test_invalid_ast import does_compile 15 | 16 | sample_dir = Path(__file__).parent / "valid_source_samples" 17 | sample_dir.mkdir(exist_ok=True) 18 | 19 | from .TestBase import TestBase 20 | 21 | 22 | class TestValidSource(TestBase): 23 | pass 24 | 25 | 26 | def gen_test(name, file): 27 | def test_valid_source(self): 28 | code = file.read_text() 29 | 30 | try: 31 | tree = ast.parse(code) 32 | except: 33 | return 34 | 35 | if not does_compile(tree): 36 | self.addDetail("the following code should be invalid:\n" + code) 37 | self.assertFalse(is_valid_ast(tree, self.addDetail), msg=self.message()) 38 | return 39 | 40 | self.addDetail("the following code should be valid:\n" + code) 41 | 42 | self.assertTrue(is_valid_ast(tree, self.addDetail), msg=self.message()) 43 | 44 | setattr(TestValidSource, "test_valid_source_" + name, test_valid_source) 45 | 46 | 47 | for file in sample_dir.glob("*.py"): 48 | gen_test(file.stem, file) 49 | 50 | 51 | def minimize_if_valid(code): 52 | def bug_found(code): 53 | try: 54 | tree = ast.parse(code) 55 | except: 56 | return False 57 | 58 | if not does_compile(tree): 59 | return False 60 | 61 | return not is_valid_ast(tree) 62 | 63 | if bug_found(code): 64 | code = minimize(code, bug_found) 65 | print() 66 | print("minimized:") 67 | print(code) 68 | name = sample_dir / f"{hashlib.sha256(code.encode('utf-8')).hexdigest()}.py" 69 | name.write_text(code) 70 | return True 71 | else: 72 | return False 73 | 74 | 75 | def generate_valid_source(seed): 76 | """ 77 | try to find source code which is valid but can not be generated by codegen() 78 | 79 | The use() hook controls if a specific restriction in the code generation should be applied 80 | 81 | """ 82 | print("seed=", seed) 83 | 84 | ignore_index = 0 85 | ignored_something = True 86 | max_index = 0 87 | 88 | while ignored_something: 89 | ignored_something = False 90 | 91 | current_index = 0 92 | 93 | def use(): 94 | nonlocal current_index 95 | nonlocal ignored_something 96 | 97 | ignore = current_index == ignore_index 98 | current_index += 1 99 | if ignore: 100 | ignored_something = True 101 | return not ignore 102 | 103 | with patch("pysource_codegen._codegen.use", use): 104 | tree = generate_ast(seed, node_limit=200, depth_limit=5) 105 | 106 | max_index = max(max_index, current_index) 107 | 108 | try: 109 | code = unparse(tree) 110 | except Exception as e: 111 | print(repr(e)) 112 | continue 113 | 114 | print(f"{seed} {ignore_index} of {max_index} ignored? {ignored_something}") 115 | 116 | if minimize_if_valid(code): 117 | return True 118 | 119 | ignore_index += 1 120 | 121 | return False 122 | 123 | 124 | if __name__ == "__main__": 125 | import json 126 | import argparse 127 | 128 | parser = argparse.ArgumentParser( 129 | description="Find valid code which can not be generated by pysource-codegen" 130 | ) 131 | parser.add_argument("folder", help="Folder to process") 132 | parser.add_argument("--skip-first", action="store_true", help="Skip the first file") 133 | args = parser.parse_args() 134 | 135 | db = Path("checked_valid_source_files") 136 | 137 | if db.exists(): 138 | checked = set(json.loads(db.read_text())) 139 | else: 140 | checked = set() 141 | 142 | globbed_files = set(map(str, Path(args.folder).rglob("*.py"))) 143 | 144 | all_files = globbed_files - checked 145 | 146 | if not all_files: 147 | all_files = globbed_files 148 | checked = set() 149 | 150 | is_first = True 151 | 152 | for file in map(Path, sorted(all_files)): 153 | print(file) 154 | try: 155 | code = file.read_text("utf-8") 156 | compile(code, str(file), "exec") 157 | except: 158 | print("skip") 159 | continue 160 | if is_first and args.skip_first: 161 | checked.add(str(file)) 162 | is_first = False 163 | continue 164 | if minimize_if_valid(code): 165 | break 166 | 167 | checked.add(str(file)) 168 | 169 | db.write_text(json.dumps(sorted(checked))) 170 | -------------------------------------------------------------------------------- /tests/test_invalid_ast.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import hashlib 3 | import sys 4 | import textwrap 5 | import warnings 6 | from pathlib import Path 7 | 8 | sys.path.append(str(Path(__file__).parent.parent.parent / "pysource-minimize" / "src")) 9 | 10 | from pysource_codegen._codegen import generate_ast 11 | from pysource_codegen._codegen import is_valid_ast 12 | from pysource_codegen._codegen import unparse 13 | from pysource_codegen._utils import ast_dump 14 | from pysource_minimize._minimize import minimize_ast 15 | from .TestBase import TestBase 16 | 17 | sample_dir = Path(__file__).parent / "invalid_ast_samples" 18 | sample_dir.mkdir(exist_ok=True) 19 | 20 | 21 | class TestInvalidAst(TestBase): 22 | 23 | def setUp(self): 24 | self.details = [] 25 | super().setUp() 26 | 27 | def addDetail(self, text): 28 | if hasattr(self, "details"): 29 | self.details.append(text) 30 | 31 | def does_compile(self, tree: ast.Module): 32 | for node in ast.walk(tree): 33 | if isinstance(node, ast.BoolOp) and len(node.values) < 2: 34 | return False 35 | if not isinstance(node, ast.JoinedStr) and any( 36 | isinstance(n, ast.FormattedValue) for n in ast.iter_child_nodes(node) 37 | ): 38 | return False 39 | try: 40 | with warnings.catch_warnings(): 41 | warnings.simplefilter("ignore", SyntaxWarning) 42 | source = unparse(tree) 43 | compile(source, "", "exec") 44 | compile(ast.fix_missing_locations(tree), "", "exec") 45 | except Exception as e: 46 | self.addDetail("exception during `compile(ast.unparse(tree))`:\n" + str(e)) 47 | return False 48 | return True 49 | 50 | 51 | does_compile = TestInvalidAst().does_compile 52 | 53 | 54 | def gen_test(name, file): 55 | def test_invalid_ast(self): 56 | code = file.read_text() 57 | self.addDetail("code:\n```\n" + code + "\n```") 58 | globals = {} 59 | try: 60 | exec(code, globals) 61 | except (NameError, ImportError) as e: 62 | 63 | return # pytest.skip(f"wrong python version {e}") 64 | 65 | tree = globals["tree"] 66 | 67 | for node in ast.walk(tree): 68 | for field in node._fields: 69 | if not hasattr(node, field): 70 | return # pytest.skip( f"wrong python version {node.__class__.__name__} is missing .{field}") 71 | if sys.version_info < (3, 8) and isinstance(node, ast.Constant): 72 | return # pytest.skip(f"ast.Constant can not be unparsed on python3.7") 73 | 74 | self.assertEqual( 75 | is_valid_ast(tree), 76 | self.does_compile(tree), 77 | msg=self.message(), 78 | ) 79 | 80 | setattr(TestInvalidAst, "test_" + name, test_invalid_ast) 81 | 82 | 83 | for file in sample_dir.glob("*.py"): 84 | gen_test(file.stem, file) 85 | 86 | 87 | def x_test_example(): 88 | seed = 2273381 89 | tree = generate_ast(seed) 90 | tree = generate_ast(seed, depth_limit=9) 91 | # print(ast.dump(tree, indent=2)) 92 | assert is_valid_ast(tree) 93 | 94 | 95 | def generate_invalid_ast(seed): 96 | print("seed =", seed) 97 | 98 | tree = generate_ast(seed, depth_limit=9) 99 | try: 100 | assert is_valid_ast(tree, print) 101 | except: 102 | print(f"The generated tree is not valid ({seed=}).") 103 | print( 104 | f"The validity is checked with the generator code which shows a problem in the generation/checking." 105 | ) 106 | return True 107 | 108 | if not does_compile(tree): 109 | last_checked_tree = tree 110 | 111 | def checker(tree): 112 | nonlocal last_checked_tree 113 | 114 | bug_found = not does_compile(tree) and is_valid_ast(tree) 115 | if bug_found: 116 | last_checked_tree = tree 117 | 118 | return bug_found 119 | 120 | try: 121 | new_tree = minimize_ast(tree, checker) 122 | except: 123 | print(f"error happend while minimize_ast seed={seed}") 124 | print(ast_dump(last_checked_tree)) 125 | raise 126 | 127 | print( 128 | "pysource-codegen thinks that the current ast produces valid python code, but this is not the case:" 129 | ) 130 | info = "from ast import *\n" 131 | info += f"tree = {ast_dump(new_tree)}\n" 132 | source = "" 133 | try: 134 | source = unparse(new_tree) 135 | compile(source, "", "exec") 136 | compile(ast.fix_missing_locations(tree), "", "exec") 137 | except Exception as e: 138 | comment = f"version: {sys.version.split()[0]}\nseed = {seed}\n\n" 139 | if source: 140 | comment += f"Source:\n{source}\n\n" 141 | comment += f"\nError:\n {e!r}" 142 | 143 | info += "\n" + textwrap.indent(comment, "# ", lambda l: True) 144 | 145 | print(info) 146 | name = sample_dir / f"{hashlib.sha256(info.encode('utf-8')).hexdigest()}.py" 147 | name.write_text(info) 148 | return True 149 | else: 150 | assert False 151 | return False 152 | 153 | 154 | if __name__ == "__main__": 155 | for i in range(0, 1000): 156 | if generate_invalid_ast(i): 157 | break 158 | -------------------------------------------------------------------------------- /tests/test_fix_nonlocal.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import sys 3 | from pathlib import Path 4 | 5 | sys.path.append(str(Path(__file__).parent.parent.parent / "pysource-minimize" / "src")) 6 | 7 | try: 8 | from inline_snapshot import snapshot 9 | except: 10 | 11 | def snapshot(x): # type: ignore 12 | return x 13 | 14 | 15 | from pysource_codegen._codegen import fix_nonlocal 16 | from pysource_codegen._codegen import unparse 17 | from pysource_codegen._utils import ast_dump 18 | 19 | known_errors = snapshot( 20 | [ 21 | "no binding for nonlocal 'x' found", 22 | "name 'x' is parameter and nonlocal", 23 | "name 'x' is used prior to nonlocal declaration", 24 | "name 'x' is assigned to before nonlocal declaration", 25 | "name 'x' is parameter and global", 26 | "name 'x' is assigned to before global declaration", 27 | "name 'x' is used prior to global declaration", 28 | "annotated name 'x' can't be global", 29 | "name 'x' is nonlocal and global", 30 | "annotated name 'x' can't be nonlocal", 31 | "nonlocal binding not allowed for type parameter 'x'", 32 | "annotated name 'name_3' can't be global", 33 | "name 'name_0' is used prior to global declaration", 34 | ] 35 | ) 36 | 37 | 38 | def check_code(src, snapshot_value): 39 | try: 40 | compile(src, "", "exec") 41 | except SyntaxError as error: 42 | error_str = str(error) 43 | assert error_str.split(" (")[0] in known_errors 44 | else: 45 | assert False, "error expected" 46 | 47 | tree = ast.parse(src) 48 | 49 | print("original tree:") 50 | print(ast_dump(tree)) 51 | print("original src:") 52 | print(src) 53 | print("error:", str(error_str)) 54 | 55 | tree = fix_nonlocal(tree) 56 | new_src = unparse(tree).strip() + "\n" 57 | 58 | print() 59 | print("transformed tree:") 60 | print(ast_dump(tree)) 61 | print("transformed src:") 62 | print(new_src) 63 | 64 | compile(new_src, "", "exec") 65 | 66 | assert new_src == snapshot_value 67 | 68 | 69 | def test_global_0(): 70 | check_code( 71 | """ 72 | def a(x): 73 | global x 74 | """, 75 | snapshot( 76 | """\ 77 | def a(x): 78 | pass 79 | """ 80 | ), 81 | ) 82 | 83 | 84 | def test_global_1(): 85 | check_code( 86 | """ 87 | def a(): 88 | x = 0 89 | global x 90 | """, 91 | snapshot( 92 | """\ 93 | def a(): 94 | x = 0 95 | pass 96 | """ 97 | ), 98 | ) 99 | 100 | 101 | def test_global_2(): 102 | check_code( 103 | """ 104 | def a(): 105 | print(x) 106 | global x 107 | """, 108 | snapshot( 109 | """\ 110 | def a(): 111 | print(x) 112 | pass 113 | """ 114 | ), 115 | ) 116 | 117 | 118 | def test_global_3(): 119 | check_code( 120 | """ 121 | def a(): 122 | x:int 123 | global x 124 | """, 125 | snapshot( 126 | """\ 127 | def a(): 128 | x: int 129 | pass 130 | """ 131 | ), 132 | ) 133 | 134 | 135 | def test_global_4(): 136 | check_code( 137 | """ 138 | 139 | def a(): 140 | x=5 141 | def b(): 142 | nonlocal x 143 | global x 144 | """, 145 | snapshot( 146 | """\ 147 | def a(): 148 | x = 5 149 | 150 | def b(): 151 | nonlocal x 152 | pass 153 | """ 154 | ), 155 | ) 156 | 157 | 158 | def test_global_5(): 159 | check_code( 160 | """ 161 | def name_4(): 162 | global name_3 163 | name_3: int 164 | """, 165 | snapshot( 166 | """\ 167 | def name_4(): 168 | global name_3 169 | pass 170 | """ 171 | ), 172 | ) 173 | 174 | 175 | def test_nonlocal_0(): 176 | check_code( 177 | """ 178 | def b(): 179 | def a(): 180 | nonlocal x 181 | """, 182 | snapshot( 183 | """\ 184 | def b(): 185 | 186 | def a(): 187 | pass 188 | """ 189 | ), 190 | ) 191 | 192 | 193 | def test_nonlocal_1(): 194 | check_code( 195 | """ 196 | def b(): 197 | x=0 198 | def a(x): 199 | nonlocal x 200 | """, 201 | snapshot( 202 | """\ 203 | def b(): 204 | x = 0 205 | 206 | def a(x): 207 | pass 208 | """ 209 | ), 210 | ) 211 | 212 | 213 | def test_nonlocal_2(): 214 | check_code( 215 | """ 216 | def b(): 217 | x=0 218 | def a(): 219 | print(x) 220 | nonlocal x 221 | """, 222 | snapshot( 223 | """\ 224 | def b(): 225 | x = 0 226 | 227 | def a(): 228 | print(x) 229 | pass 230 | """ 231 | ), 232 | ) 233 | 234 | 235 | def test_nonlocal_3(): 236 | check_code( 237 | """ 238 | def b(): 239 | x=0 240 | def a(): 241 | x=5 242 | nonlocal x 243 | """, 244 | snapshot( 245 | """\ 246 | def b(): 247 | x = 0 248 | 249 | def a(): 250 | x = 5 251 | pass 252 | """ 253 | ), 254 | ) 255 | 256 | 257 | def test_nonlocal_4(): 258 | check_code( 259 | """ 260 | def b(): 261 | x=0 262 | def a(): 263 | x:int 264 | nonlocal x 265 | """, 266 | snapshot( 267 | """\ 268 | def b(): 269 | x = 0 270 | 271 | def a(): 272 | x: int 273 | pass 274 | """ 275 | ), 276 | ) 277 | 278 | 279 | def test_nonlocal_5(): 280 | if sys.version_info >= (3, 12): 281 | check_code( 282 | """ 283 | def b(): 284 | x=0 285 | def a[x:int](): 286 | nonlocal x 287 | """, 288 | snapshot( 289 | """\ 290 | def b(): 291 | x = 0 292 | 293 | def a[x: int](): 294 | pass 295 | """ 296 | ), 297 | ) 298 | -------------------------------------------------------------------------------- /run_all.py: -------------------------------------------------------------------------------- 1 | # /// script 2 | # requires-python = ">=3.10" 3 | # dependencies = [ 4 | # "rich", 5 | # ] 6 | # /// 7 | import concurrent.futures 8 | import json 9 | import re 10 | import subprocess 11 | 12 | from rich.console import Console 13 | from rich.table import Table 14 | 15 | 16 | def parse_version(s): 17 | """ 18 | Returns (major, minor, patch, pre_type, pre_num) 19 | pre_type: None, 'a', or 'rc' 20 | pre_num: int or None 21 | """ 22 | m = re.match(r"(\d+)\.(\d+)\.(\d+)(?:(a|rc)(\d+))?", s) 23 | if not m: 24 | raise ValueError(f"Invalid version string: {s}") 25 | major, minor, patch = map(int, m.group(1, 2, 3)) 26 | pre_type = m.group(4) 27 | pre_num = int(m.group(5)) if m.group(5) else None 28 | return (major, minor, patch, pre_type, pre_num) 29 | 30 | 31 | def version_tuple(s): 32 | # Remove rc or alpha suffixes if present for stable comparison 33 | v = parse_version(s) 34 | return (v[0], v[1], v[2]) 35 | 36 | 37 | def pre_release_key(s): 38 | v = parse_version(s) 39 | # None < a < rc, and lower numbers first 40 | if v[3] is None: 41 | return (0, 0) # stable 42 | elif v[3] == "a": 43 | return (1, v[4] or 0) 44 | elif v[3] == "rc": 45 | return (2, v[4] or 0) 46 | else: 47 | return (3, 0) # unknown, sort last 48 | 49 | 50 | result = subprocess.run( 51 | ["uv", "python", "list", "--all-versions", "--output-format=json"], 52 | capture_output=True, 53 | ) 54 | 55 | versions = set() 56 | for e in json.loads(result.stdout): 57 | if e["implementation"] != "cpython": 58 | continue 59 | versions.add(e["version"]) 60 | 61 | # Group versions by minor version and type 62 | stable = {} 63 | pre = {} 64 | 65 | for v in versions: 66 | vt = version_tuple(v) 67 | minor = f"{vt[0]}.{vt[1]}" 68 | pv = parse_version(v) 69 | if pv[3] is not None: 70 | # pre-release: keep only the most recent per minor 71 | if minor not in pre or pre_release_key(v) > pre_release_key(pre[minor]): 72 | pre[minor] = v 73 | else: 74 | stable.setdefault(minor, []).append(v) 75 | 76 | # Collect all stable versions and the most recent pre-release per minor 77 | filtered_versions = [] 78 | for minor in stable: 79 | filtered_versions.extend(stable[minor]) 80 | for minor in pre: 81 | filtered_versions.append(pre[minor]) 82 | 83 | versions = sorted( 84 | [v for v in filtered_versions if version_tuple(v) >= (3, 8)], key=version_tuple 85 | ) 86 | 87 | 88 | def run_test(version): 89 | result = subprocess.run( 90 | ["uvx", "-p", version, "--with-editable=.", "python", "-m", "unittest", "-v"], 91 | capture_output=True, 92 | text=True, 93 | ) 94 | fail_cmds = [] 95 | for line in result.stderr.splitlines(): 96 | if line.startswith("FAIL: "): 97 | # Example line: FAIL: test_func (module.Class) 98 | fail_info = line.split() 99 | test_func = fail_info[1] 100 | module_class = fail_info[2].strip("()") 101 | if not module_class.endswith(test_func): 102 | module_class += "." + test_func 103 | 104 | fail_cmds.append(f"uvx -p {version} python -m unittest {module_class}") 105 | return version, result.returncode, fail_cmds 106 | 107 | 108 | results = {} 109 | fail_commands = [] 110 | 111 | with concurrent.futures.ThreadPoolExecutor() as executor: 112 | futures = {executor.submit(run_test, version): version for version in versions} 113 | for future in concurrent.futures.as_completed(futures): 114 | version, returncode, fail_cmds = future.result() 115 | results[version] = returncode 116 | for cmd in fail_cmds: 117 | print(cmd) 118 | 119 | # results={'3.9.10': 0, '3.11.3': 0, '3.13.4': 0, '3.10.17': 0, '3.11.8': 0, '3.10.16': 0, '3.12.4': 1, '3.8.11': 0, '3.10.0': 0, '3.9.22': 0, '3.12.2': 1, '3.13.3': 0, '3.8.9': 0, '3.11.11': 0, '3.10.15': 0, '3.9.4': 0, '3.8.14': 0, '3.8.20': 0, '3.12.7': 1, '3.9.12': 0, '3.9.16': 0, '3.11.13': 0, '3.9.7': 0, '3.9.15': 0, '3.12.5': 1, '3.12.11': 1, '3.8.19': 0, '3.10.14': 0, '3.10.4': 0, '3.9.5': 0, '3.9.21': 0, '3.9.0': 1, '3.11.1': 0, '3.10.9': 0, '3.9.17': 0, '3.12.6': 1, '3.10.2': 0, '3.11.4': 0, '3.13.0': 0, '3.12.1': 1, '3.11.12': 0, '3.10.8': 0, '3.8.3': 0, '3.9.6': 0, '3.9.18': 0, '3.10.13': 0, '3.8.15': 0, '3.9.13': 0, '3.11.6': 0, '3.8.12': 0, '3.11.2': 0, '3.13.1': 0, '3.8.13': 0, '3.8.2': 0, '3.8.18': 0, '3.9.11': 0, '3.12.3': 1, '3.8.5': 0, '3.8.10': 0, '3.12.8': 1, '3.13.2': 0, '3.8.16': 0, '3.10.11': 0, '3.7.9': 1, '3.9.1': 1, '3.8.17': 0, '3.10.6': 0, '3.13.6': 0, '3.10.5': 0, '3.10.7': 0, '3.11.9': 0, '3.9.19': 0, '3.12.10': 1, '3.8.8': 0, '3.13.5': 0, '3.9.20': 0, '3.9.14': 0, '3.12.9': 1, '3.8.7': 0, '3.12.0': 1, '3.8.6': 0, '3.10.18': 0, '3.11.7': 0, '3.11.5': 0, '3.10.12': 0, '3.9.3': 0, '3.10.3': 0, '3.11.10': 0, '3.9.23': 0, '3.9.2': 1} 120 | 121 | # Group results by minor version 122 | minor_versions = {} 123 | for version, returncode in results.items(): 124 | minor = ".".join(version.split(".")[:2]) 125 | minor_versions.setdefault(minor, []).append((version, returncode)) 126 | 127 | # Sort minor versions and versions within each minor 128 | sorted_minor_versions = sorted( 129 | minor_versions.keys(), key=lambda v: tuple(map(int, v.split("."))) 130 | ) 131 | for minor in sorted_minor_versions: 132 | minor_versions[minor].sort(key=lambda x: version_tuple(x[0])) 133 | 134 | # Prepare table 135 | table = Table(title="Python Test Results by Minor Version") 136 | for minor in sorted_minor_versions: 137 | table.add_column(minor, justify="left") 138 | 139 | # Find max number of patch versions in any minor version 140 | max_rows = max(len(v) for v in minor_versions.values()) 141 | 142 | # Fill table rows 143 | for i in range(max_rows): 144 | row = [] 145 | for minor in sorted_minor_versions: 146 | if i < len(minor_versions[minor]): 147 | version, returncode = minor_versions[minor][i] 148 | status = "[green]ok[/green]" if returncode == 0 else "[red]failed[/red]" 149 | row.append(f"{version} {status}") 150 | else: 151 | row.append("") 152 | table.add_row(*row) 153 | 154 | console = Console() 155 | console.print(table) 156 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/6adfa013a6d77e2d3724197af1ef899b3d909c7b22bb128010a03d4736a11986.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import BinOp 4 | from ast import comprehension 5 | from ast import Constant 6 | from ast import Dict 7 | from ast import Expr 8 | from ast import FormattedValue 9 | from ast import GeneratorExp 10 | from ast import IfExp 11 | from ast import JoinedStr 12 | from ast import List 13 | from ast import Load 14 | from ast import Mod 15 | from ast import Module 16 | from ast import Name 17 | from ast import Set 18 | from ast import SetComp 19 | from ast import Store 20 | from ast import Tuple 21 | from ast import UnaryOp 22 | from ast import USub 23 | from ast import YieldFrom 24 | 25 | tree = Module( 26 | body=[ 27 | AsyncFunctionDef( 28 | name="name_4", 29 | args=arguments( 30 | posonlyargs=[], 31 | args=[], 32 | kwonlyargs=[], 33 | kw_defaults=[ 34 | SetComp( 35 | elt=Name(id="name_3", ctx=Load()), 36 | generators=[ 37 | comprehension( 38 | target=Tuple( 39 | elts=[Name(id="name_0", ctx=Store())], ctx=Store() 40 | ), 41 | iter=Dict( 42 | keys=[Name(id="name_3", ctx=Load())], 43 | values=[Name(id="name_2", ctx=Load())], 44 | ), 45 | ifs=[ 46 | BinOp( 47 | left=Name(id="name_0", ctx=Load()), 48 | op=Mod(), 49 | right=Name(id="name_5", ctx=Load()), 50 | ) 51 | ], 52 | is_async=0, 53 | ), 54 | comprehension( 55 | target=List( 56 | elts=[Name(id="name_0", ctx=Store())], ctx=Store() 57 | ), 58 | iter=Dict( 59 | keys=[Name(id="name_5", ctx=Load())], 60 | values=[Name(id="name_1", ctx=Load())], 61 | ), 62 | ifs=[ 63 | JoinedStr( 64 | values=[ 65 | FormattedValue( 66 | value=Name(id="name_4", ctx=Load()), 67 | conversion=-1, 68 | ) 69 | ] 70 | ), 71 | Set(elts=[Name(id="name_3", ctx=Load())]), 72 | UnaryOp( 73 | op=USub(), operand=Name(id="name_1", ctx=Load()) 74 | ), 75 | ], 76 | is_async=0, 77 | ), 78 | comprehension( 79 | target=Name(id="name_5", ctx=Store()), 80 | iter=IfExp( 81 | test=Name(id="name_1", ctx=Load()), 82 | body=Name(id="name_4", ctx=Load()), 83 | orelse=Name(id="name_0", ctx=Load()), 84 | ), 85 | ifs=[ 86 | GeneratorExp( 87 | elt=Name(id="name_3", ctx=Load()), 88 | generators=[ 89 | comprehension( 90 | target=Name(id="name_1", ctx=Store()), 91 | iter=Name(id="name_5", ctx=Load()), 92 | ifs=[Name(id="name_0", ctx=Load())], 93 | is_async=0, 94 | ) 95 | ], 96 | ) 97 | ], 98 | is_async=0, 99 | ), 100 | ], 101 | ), 102 | JoinedStr( 103 | values=[ 104 | Constant(value="text"), 105 | FormattedValue( 106 | value=Name(id="name_1", ctx=Load()), 107 | conversion=97, 108 | format_spec=JoinedStr( 109 | values=[ 110 | FormattedValue( 111 | value=Name(id="name_4", ctx=Load()), 112 | conversion=115, 113 | format_spec=JoinedStr( 114 | values=[Constant(value="text")] 115 | ), 116 | ) 117 | ] 118 | ), 119 | ), 120 | FormattedValue( 121 | value=Name(id="name_2", ctx=Load()), conversion=-1 122 | ), 123 | ] 124 | ), 125 | ], 126 | defaults=[], 127 | ), 128 | body=[Expr(value=YieldFrom(value=Name(id="name_5", ctx=Load())))], 129 | decorator_list=[], 130 | type_params=[], 131 | ) 132 | ], 133 | type_ignores=[], 134 | ) 135 | 136 | # version: 3.12.0 137 | # 138 | # Source: 139 | # async def name_4(): 140 | # yield from name_5 141 | # 142 | # 143 | # Error: 144 | # SyntaxError("'yield from' inside async function", ('', 2, 5, None, 2, 22)) 145 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.7.1 (2025-08-29) 2 | 3 | ### Fix 4 | 5 | - added entry point for pysource-codegen 6 | 7 | ## v0.7.0 (2025-08-29) 8 | 9 | ### Feat 10 | 11 | - **3.14**: added support for template-strings 12 | - use unittest to be able to test cpython main 13 | - work with cpython/main 14 | 15 | ### Fix 16 | 17 | - fixed type errors 18 | - fixed 3.14 bugs 19 | - **3.9**: an empty set can not be unparsed in cpython < 3.9.3 20 | - add byte literals with characters up to utf-32be 21 | 22 | ## v0.6.0 (2024-10-09) 23 | 24 | ### Feat 25 | 26 | - more variation in string constants 27 | - start to support 3.13 28 | 29 | ### Fix 30 | 31 | - fixed typing 32 | - correct probability spelling 33 | - { and } are not allowed in formatspec, because they can not be escaped 34 | - asynchronous comprehension in TypeVar.bound 35 | - asynchronous comprehension outside of an asynchronous function 36 | - no async code in type annotations 37 | - the same varibale can be declared global multiple times 38 | - Starred expression inside ClassDef.bases 39 | - named expression cannot be used within a TypeVar bound 40 | - no async generators in type aliases 41 | - allow await in generator expressions 42 | - dict comprehension is a valid base class 43 | - named expression is a valid base class 44 | - allow SetComp in type-scope 45 | - lambda is valid inside annotations 46 | - made generator expressions valid inside class bases and type annotations 47 | - allow ListComp in type-scope 48 | - named-expressions inside list-comprehensions use the outside scope 49 | - restrict match value patterns 50 | - variable can not be nonlocal if it is declared global in the parent scope 51 | - count MatchStar variables as none usable global names 52 | - non default type params before type params with default values 53 | 54 | ## v0.5.2 (2024-04-23) 55 | 56 | ### Fix 57 | 58 | - Explicitly re-export `generate()` in `__init__.py` 59 | 60 | ## v0.5.1 (2024-01-23) 61 | 62 | ### Fix 63 | 64 | - add no type_comment when changing AnnAssign to Assign 65 | - **3.8**: ExtSlice can not be nested 66 | - no lambda in annotation scope within class scope 67 | - use None MatchSingleton for invalid match expressions 68 | - do not change the order of MatchClass.kwd_attrs 69 | - correct handling of MatchAs without a name 70 | - MatchSingleton can only be used for None, True or False 71 | - Assign.target needs a minimal length of 1 72 | - AnnAssign can only be simple if the target is a Name 73 | - MatchValue can not be used for None, True or False 74 | - AugAssign requires Store context for target 75 | - TypeAlias.name requires a Store context 76 | - Dict .keys and .values has the same number of elements 77 | - Compare .ops and .comparators has the same number of elementes 78 | - use correct .ctx for nodes 79 | - analyse rest in mapping-pattern as written variable 80 | - **global**: analyse used variables in annotations 81 | - allow only except handler with a type in try-star blocks 82 | 83 | ### Refactor 84 | 85 | - moved unparse to utils 86 | 87 | ## v0.5.0 (2023-11-29) 88 | 89 | ### Feat 90 | 91 | - removed 3.7 support 92 | - constant float, int and bytes values 93 | - removed 3.7 support 94 | 95 | ### Fix 96 | 97 | - a variable can not be nonlocal and global 98 | - workaround for https://github.com/python/cpython/issues/111123 99 | - correct handling of used names in SetComp 100 | - check for yield in return-annotation in a inner function of an async function which returns a value 101 | - use correct minimal length for lists of child nodes 102 | - Starred expression inside AnnAssign target is not allowed 103 | - correct handling of used names in DictComp 104 | - an annotated varible can not be declared nonlocal 105 | - global declaration of a used name in a return annotation is not allowed 106 | - global declaration of a class in the current scope is not allowed 107 | - **3.8**: use correct node types for Slices 108 | - kwonlyargs and kw_defaults has to have the same size 109 | - handle default arguments in the scope where the function is defined 110 | - handle Slice and Starred in Subscript correctly 111 | - **yield**: correct handling of code which is part of a function (yield inside decorator) 112 | - handle MatchAs as name which can be used with global/nonlocal 113 | - class variables can not be accessed by nonlocal from child scope 114 | - correct handling of used names in ListComp 115 | - **global**: check for used names in comprehension.iter 116 | - **global**: use of names in annotations is not allowed before their global declaration 117 | - **nonlocal**: do not search for assigned variables inside genarator expressions 118 | - allow negative constants as match values 119 | - allow all expression as match values 120 | - handle wildcard with guard clause correct 121 | - allow to match constants 122 | - allow return with value in async function without yield, but inner generator function 123 | - allow async GeneratorExp in non async code 124 | - allow break in async for 125 | - allow nonlocal definition of one variable twice 126 | - allow global definition of one variable twice 127 | - allow nonlocal of variables which are only deleted in the parent scope 128 | - allow `nonlocal __class__` in methods 129 | - allow Nonlocal in ClassDef 130 | - support Slice ExtSlice and Index for 3.8 and 3.7 131 | - allow Subscript as AnnAssign target 132 | - allow yield inside lambda 133 | - generate code like `del(a,[b,c])` 134 | - generate correct AnnAssign 135 | - allow raise at module level 136 | - string constants issues 137 | - allow continue outside of functions 138 | - allow empty modules 139 | - allow annotation of attribute assignments 140 | - recreated poetry.lock 141 | 142 | ### Refactor 143 | 144 | - created ast_dump 145 | - removed the internal use of compile() 146 | 147 | ## v0.4.3 (2023-11-12) 148 | 149 | ### Fix 150 | 151 | - remove upper bound from dependencies in pyproject.toml 152 | 153 | ## v0.4.2 (2023-10-07) 154 | 155 | ### Fix 156 | 157 | - filter syntax warnings 158 | 159 | ## v0.4.1 (2023-10-01) 160 | 161 | ### Fix 162 | 163 | - fix nonlocal/global handling 164 | 165 | ## v0.4.0 (2023-10-01) 166 | 167 | ### Feat 168 | 169 | - 3.12 support 170 | 171 | ### Fix 172 | 173 | - **3.7**: continue is not allowed in finally clause 174 | 175 | ### Refactor 176 | 177 | - remove compile() 178 | 179 | ## v0.3.0 (2023-09-05) 180 | 181 | ## v0.2.0 (2023-08-05) 182 | 183 | ### Feat 184 | 185 | - 3.7 support 186 | - typing 187 | - 3.8 support 188 | - 3.9 support 189 | - 3.10 support 190 | 191 | ### Fix 192 | 193 | - typing & updated pre-commit checks 194 | 195 | ## v0.1.0 (2023-05-11) 196 | 197 | ### Feat 198 | 199 | - added cli interface 200 | 201 | ### Fix 202 | 203 | - changed probabilities to generate more expressions 204 | -------------------------------------------------------------------------------- /tests/invalid_ast_samples/869b220024e2220d80c6777187ef1e6f1f424ed4a5b2b84f8463eaf09e3642a0.py: -------------------------------------------------------------------------------- 1 | from ast import arguments 2 | from ast import AsyncFunctionDef 3 | from ast import Await 4 | from ast import Call 5 | from ast import ClassDef 6 | from ast import Compare 7 | from ast import comprehension 8 | from ast import Constant 9 | from ast import Eq 10 | from ast import Expr 11 | from ast import GeneratorExp 12 | from ast import IfExp 13 | from ast import Invert 14 | from ast import IsNot 15 | from ast import keyword 16 | from ast import ListComp 17 | from ast import Load 18 | from ast import Lt 19 | from ast import Module 20 | from ast import Name 21 | from ast import NamedExpr 22 | from ast import Store 23 | from ast import Subscript 24 | from ast import Tuple 25 | from ast import UnaryOp 26 | 27 | tree = Module( 28 | body=[ 29 | AsyncFunctionDef( 30 | name="name_0", 31 | args=arguments( 32 | posonlyargs=[], 33 | args=[], 34 | kwonlyargs=[], 35 | kw_defaults=[ 36 | Compare( 37 | left=IfExp( 38 | test=Name(id="name_1", ctx=Load()), 39 | body=Name(id="name_0", ctx=Load()), 40 | orelse=Name(id="name_5", ctx=Load()), 41 | ), 42 | ops=[Lt(), Lt(), IsNot()], 43 | comparators=[ 44 | NamedExpr( 45 | target=Name(id="name_3", ctx=Load()), 46 | value=Name(id="name_2", ctx=Load()), 47 | ), 48 | UnaryOp(op=Invert(), operand=Name(id="name_4", ctx=Load())), 49 | Constant(value=""), 50 | ListComp( 51 | elt=Name(id="name_2", ctx=Load()), 52 | generators=[ 53 | comprehension( 54 | target=Name(id="name_1", ctx=Store()), 55 | iter=Name(id="name_5", ctx=Load()), 56 | ifs=[Name(id="name_2", ctx=Load())], 57 | is_async=0, 58 | ), 59 | comprehension( 60 | target=Name(id="name_0", ctx=Store()), 61 | iter=Name(id="name_3", ctx=Load()), 62 | ifs=[Name(id="name_5", ctx=Load())], 63 | is_async=0, 64 | ), 65 | comprehension( 66 | target=Name(id="name_3", ctx=Store()), 67 | iter=Name(id="name_4", ctx=Load()), 68 | ifs=[Name(id="name_3", ctx=Load())], 69 | is_async=0, 70 | ), 71 | ], 72 | ), 73 | IfExp( 74 | test=Name(id="name_0", ctx=Load()), 75 | body=Name(id="name_1", ctx=Load()), 76 | orelse=Name(id="name_5", ctx=Load()), 77 | ), 78 | ], 79 | ), 80 | Call( 81 | func=NamedExpr( 82 | target=Name(id="name_0", ctx=Load()), 83 | value=Name(id="name_5", ctx=Load()), 84 | ), 85 | args=[ 86 | Tuple( 87 | elts=[ 88 | Name(id="name_4", ctx=Load()), 89 | Name(id="name_3", ctx=Load()), 90 | Name(id="name_4", ctx=Load()), 91 | ], 92 | ctx=Load(), 93 | ), 94 | Subscript( 95 | value=Name(id="name_4", ctx=Load()), 96 | slice=Name(id="name_1", ctx=Load()), 97 | ctx=Load(), 98 | ), 99 | Compare( 100 | left=Name(id="name_1", ctx=Load()), 101 | ops=[Eq(), IsNot()], 102 | comparators=[ 103 | Name(id="name_3", ctx=Load()), 104 | Name(id="name_4", ctx=Load()), 105 | Name(id="name_4", ctx=Load()), 106 | Name(id="name_2", ctx=Load()), 107 | ], 108 | ), 109 | ], 110 | keywords=[ 111 | keyword( 112 | arg="name_4", 113 | value=GeneratorExp( 114 | elt=Name(id="name_3", ctx=Load()), 115 | generators=[ 116 | comprehension( 117 | target=Name(id="name_2", ctx=Store()), 118 | iter=Name(id="name_3", ctx=Load()), 119 | ifs=[Name(id="name_5", ctx=Load())], 120 | is_async=0, 121 | ) 122 | ], 123 | ), 124 | ) 125 | ], 126 | ), 127 | ], 128 | defaults=[], 129 | ), 130 | body=[ 131 | ClassDef( 132 | name="name_4", 133 | bases=[], 134 | keywords=[], 135 | body=[Expr(value=Await(value=Name(id="name_5", ctx=Load())))], 136 | decorator_list=[], 137 | type_params=[], 138 | ) 139 | ], 140 | decorator_list=[], 141 | type_params=[], 142 | ) 143 | ], 144 | type_ignores=[], 145 | ) 146 | 147 | # version: 3.12.0 148 | # 149 | # Source: 150 | # async def name_0(): 151 | # 152 | # class name_4: 153 | # await name_5 154 | # 155 | # 156 | # Error: 157 | # SyntaxError("'await' outside function", ('', 4, 9, None, 4, 21)) 158 | --------------------------------------------------------------------------------