├── app └── index.html ├── .gitignore ├── pytest.ini ├── src └── spew │ ├── __init__.py │ ├── randomcycle.py │ ├── names.py │ ├── __main__.py │ └── generate.py ├── api ├── function.py └── function.json ├── .vscode └── settings.json ├── pyproject.toml ├── LICENSE ├── .github └── workflows │ ├── python-package.yml │ └── azure-static-web-apps-kind-dune-0f668560f.yml ├── README.md ├── tests └── test_generate.py └── examples └── output_default.py /app/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [.]venv/ 2 | *.py[co] 3 | dist/ -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 6.0 3 | addopts = --count=10 4 | testpaths = tests 5 | -------------------------------------------------------------------------------- /src/spew/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Spew - A generator for Python. It uses Python to generate valid Python code. 3 | """ 4 | 5 | __version__ = "1.0.2" 6 | -------------------------------------------------------------------------------- /api/function.py: -------------------------------------------------------------------------------- 1 | import azure.functions as func 2 | import logging 3 | 4 | 5 | def main(req: func.HttpRequest) -> func.HttpResponse: 6 | logging.info("Python HTTP trigger function processed a request.") 7 | return func.HttpResponse("Hello, world!") 8 | -------------------------------------------------------------------------------- /api/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "scriptFile": "function.py", 3 | "bindings": [ 4 | { 5 | "authLevel": "anonymous", 6 | "type": "httpTrigger", 7 | "direction": "in", 8 | "name": "req", 9 | "methods": [ 10 | "post" 11 | ] 12 | }, 13 | { 14 | "type": "http", 15 | "direction": "out", 16 | "name": "$return" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.testing.pytestArgs": [ 3 | "tests" 4 | ], 5 | "python.testing.unittestEnabled": false, 6 | "python.testing.pytestEnabled": true, 7 | "python.testing.cwd": "${workspaceFolder}", 8 | "python.analysis.diagnosticMode": "workspace", 9 | "python.analysis.typeCheckingMode": "basic", 10 | "[python]": { 11 | "editor.formatOnSave": true, 12 | } 13 | } -------------------------------------------------------------------------------- /src/spew/randomcycle.py: -------------------------------------------------------------------------------- 1 | import random as _random 2 | import typing 3 | 4 | TCycle = typing.TypeVar("TCycle") 5 | 6 | 7 | # Define a cycle that yields a random element in a list until it is exhausted then starts again 8 | def rcycle(l: typing.Iterable[TCycle]) -> typing.Generator[TCycle, None, None]: 9 | while True: 10 | items = list(l) 11 | _random.shuffle(items) 12 | for item in items: 13 | yield item 14 | -------------------------------------------------------------------------------- /src/spew/names.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | FIRST_CHARS = "abcdefghijklmnopqrstuvwxyz" 4 | OTHER_CHARS = "abcdefghijklmnopqrstuvwxyz1234567890_" 5 | 6 | 7 | def generate(ctx, new: bool = False) -> str: 8 | # TODO: create unique names 9 | if not new and ctx.names: 10 | return random.choice(ctx.names) 11 | 12 | name = random.choice(FIRST_CHARS) 13 | for _ in range(10): 14 | name += random.choice(OTHER_CHARS) 15 | ctx.names.append(name) 16 | return name 17 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.2,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "spew" 7 | authors = [{name = "Anthony Shaw"}] 8 | license = {file = "LICENSE"} 9 | classifiers = ["License :: OSI Approved :: MIT License"] 10 | dynamic = ["version", "description"] 11 | requires-python = ">=3.10" 12 | dependencies = ["rich"] 13 | 14 | [project.optional-dependencies] 15 | test = [ 16 | "pytest", 17 | "pytest-repeat", 18 | ] 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Anthony Shaw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.10", "3.11", "3.12-dev"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip flit pytest-cov codecov 30 | flit install --deps=all 31 | - name: Test with pytest 32 | run: | 33 | pytest 34 | - name: Run tests and collect coverage 35 | run: pytest --cov=spew --cov-report=xml 36 | - name: Upload coverage reports to Codecov with GitHub Action 37 | uses: codecov/codecov-action@v3 38 | env: 39 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 40 | -------------------------------------------------------------------------------- /src/spew/__main__.py: -------------------------------------------------------------------------------- 1 | import spew.generate 2 | import ast 3 | from rich.console import Console 4 | from rich.syntax import Syntax 5 | import argparse 6 | import logging 7 | 8 | logging.basicConfig(level=logging.INFO) 9 | logger = logging.getLogger(__name__) 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument( 13 | "--depth", type=int, default=4, help="Maximum depth (nesting) of the module" 14 | ) 15 | parser.add_argument("--width", type=int, default=10) 16 | parser.add_argument("--log-level", type=str, default="INFO") 17 | parser.add_argument( 18 | "--output", 19 | type=argparse.FileType("w", encoding="utf-8"), 20 | default=None, 21 | help="Output file. If not specified, the output will be printed to the console.", 22 | ) 23 | parser.add_argument( 24 | "--check", action="store_true", help="Check if the code is valid Python" 25 | ) 26 | args = parser.parse_args() 27 | 28 | console = Console() 29 | logger.setLevel(args.log_level) 30 | logger.debug("Generating module with depth %s and width %s", args.depth, args.width) 31 | m = spew.generate.generate_module( 32 | depth=args.depth, width=args.width, log_level=args.log_level 33 | ) 34 | code = ast.unparse(m) 35 | 36 | 37 | if args.output: 38 | args.output.write(code) 39 | else: 40 | syntax = Syntax(code, "python") 41 | console.print(syntax) 42 | 43 | if args.check: 44 | ast.parse(code, "test.py") 45 | logger.info("Code is valid Python") 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spew 2 | 3 | A tool for generating random, syntactically-correct Python code. Designed for fuzzing and testing of tools that parse Python code. 4 | 5 | Designed for Python 3.10 and above, this is for testing the latest syntax. 6 | 7 | Supports: 8 | 9 | - Functions 10 | - For, If, While 11 | - Async With, For, FunctionDef 12 | - All constant expressions 13 | - Match statements 14 | - Many expression types 15 | - Decorators 16 | - Classes 17 | 18 | ## Usage 19 | 20 | You can use at the command line: 21 | 22 | ```console 23 | > python -m spew --depth=4 24 | ``` 25 | 26 | Caution, depths higher than 5 creating a huge recursive computing load. (A depth of 6 creates a file ~40,000 lines of code.) 27 | 28 | Also, you can generate specific nodes, like modules or functions: 29 | 30 | ```python 31 | import spew.generate as g 32 | 33 | context = g.Context() 34 | func = g.generate_function(context) # returns an ast.FunctionDef object 35 | ``` 36 | 37 | To generate AST objects back into Python code you can use the `ast.unparse()` function. 38 | 39 | The full list of command-line options: 40 | 41 | ```default 42 | python -m spew --help 43 | usage: __main__.py [-h] [--depth DEPTH] [--width WIDTH] [--log-level LOG_LEVEL] [--output OUTPUT] [--check] 44 | 45 | options: 46 | -h, --help show this help message and exit 47 | --depth DEPTH Maximum depth (nesting) of the module 48 | --width WIDTH 49 | --log-level LOG_LEVEL 50 | --output OUTPUT Output file. If not specified, the output will be printed to the console. 51 | --check Check if the code is valid Python 52 | ``` 53 | -------------------------------------------------------------------------------- /.github/workflows/azure-static-web-apps-kind-dune-0f668560f.yml: -------------------------------------------------------------------------------- 1 | name: Azure Static Web Apps CI/CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [opened, synchronize, reopened, closed] 9 | branches: 10 | - master 11 | 12 | jobs: 13 | build_and_deploy_job: 14 | if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') 15 | runs-on: ubuntu-latest 16 | name: Build and Deploy Job 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | submodules: true 21 | - name: Build And Deploy 22 | id: builddeploy 23 | uses: Azure/static-web-apps-deploy@v1 24 | with: 25 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_KIND_DUNE_0F668560F }} 26 | repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) 27 | action: "upload" 28 | ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### 29 | # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig 30 | app_location: "app" # App source code path 31 | api_location: "api" # Api source code path - optional 32 | # output_location: "/" # Built app content directory - optional 33 | ###### End of Repository/Build Configurations ###### 34 | 35 | close_pull_request_job: 36 | if: github.event_name == 'pull_request' && github.event.action == 'closed' 37 | runs-on: ubuntu-latest 38 | name: Close Pull Request Job 39 | steps: 40 | - name: Close Pull Request 41 | id: closepullrequest 42 | uses: Azure/static-web-apps-deploy@v1 43 | with: 44 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_KIND_DUNE_0F668560F }} 45 | action: "close" 46 | -------------------------------------------------------------------------------- /tests/test_generate.py: -------------------------------------------------------------------------------- 1 | import spew.generate as g 2 | import ast 3 | import pytest 4 | import compileall 5 | import tempfile 6 | 7 | 8 | @pytest.fixture 9 | def ctx(): 10 | context = g.Context() 11 | context.max_depth = 1 12 | return context 13 | 14 | 15 | def compiles(code: str): 16 | ast.parse(code) 17 | with tempfile.NamedTemporaryFile("w", suffix=".py") as f: 18 | f.write(code) 19 | result = compileall.compile_file(f.name) 20 | return result 21 | 22 | 23 | @pytest.mark.parametrize("depth", [1, 2, 3, 4, 5]) 24 | def test_nested_generate_module(depth): 25 | module = g.generate_module(depth=depth, width=3) 26 | assert module 27 | code = ast.unparse(module) 28 | assert compiles(code) 29 | 30 | 31 | def test_generate_arg(ctx): 32 | arg = g.generate_arg(ctx) 33 | assert arg 34 | code = ast.unparse(arg) 35 | assert compiles(code) 36 | 37 | 38 | def test_generate_function(ctx): 39 | ctx.max_depth = 1 40 | func = g.generate_function(ctx) 41 | assert func 42 | code = ast.unparse(func) 43 | assert compiles(code) 44 | 45 | 46 | def test_generate_asyncfunction(ctx): 47 | ctx.max_depth = 1 48 | func = g.generate_asyncfunction(ctx) 49 | assert func 50 | code = ast.unparse(func) 51 | assert compiles(code) 52 | 53 | 54 | def test_generate_class(ctx): 55 | ctx.max_depth = 1 56 | class_ = g.generate_class(ctx) 57 | assert class_ 58 | code = ast.unparse(class_) 59 | assert compiles(code) 60 | 61 | 62 | def test_generate_ellipsis(ctx): 63 | ellipsis = g.generate_ellipsis(ctx) 64 | assert ellipsis 65 | code = ast.unparse(ellipsis) 66 | assert compiles(code) 67 | 68 | 69 | def test_generate_pass(ctx): 70 | pass_ = g.generate_pass(ctx) 71 | assert pass_ 72 | code = ast.unparse(pass_) 73 | assert compiles(code) 74 | 75 | 76 | def test_generate_break(ctx): 77 | break_ = g.generate_break(ctx) 78 | assert break_ 79 | code = ast.unparse(break_) 80 | assert compiles(code) 81 | 82 | 83 | def test_generate_continue(ctx): 84 | continue_ = g.generate_continue(ctx) 85 | assert continue_ 86 | code = ast.unparse(continue_) 87 | assert compiles(code) 88 | 89 | 90 | def test_generate_assign(ctx): 91 | assign = g.generate_assign(ctx) 92 | assert assign 93 | code = ast.unparse(assign) 94 | assert compiles(code) 95 | 96 | 97 | def test_generate_augassign(ctx): 98 | augassign = g.generate_augassign(ctx) 99 | assert augassign 100 | code = ast.unparse(augassign) 101 | assert compiles(code) 102 | 103 | 104 | def test_generate_annassign(ctx): 105 | annassign = g.generate_annassign(ctx) 106 | assert annassign 107 | code = ast.unparse(annassign) 108 | assert compiles(code) 109 | 110 | 111 | def test_generate_import(ctx): 112 | import_ = g.generate_import(ctx) 113 | assert import_ 114 | code = ast.unparse(import_) 115 | assert compiles(code) 116 | 117 | 118 | def test_generate_importfrom(ctx): 119 | importfrom = g.generate_importfrom(ctx) 120 | assert importfrom 121 | code = ast.unparse(importfrom) 122 | assert compiles(code) 123 | 124 | 125 | def test_generate_name(ctx): 126 | name = g.generate_name(ctx) 127 | assert name 128 | code = ast.unparse(name) 129 | assert compiles(code) 130 | 131 | 132 | def test_generate_constant(ctx): 133 | constant = g.generate_constant(ctx) 134 | assert constant 135 | code = ast.unparse(constant) 136 | assert compiles(code) 137 | 138 | 139 | def test_generate_delete(ctx): 140 | delete = g.generate_delete(ctx) 141 | assert delete 142 | code = ast.unparse(delete) 143 | assert compiles(code) 144 | 145 | 146 | def test_generate_raise(ctx): 147 | raise_ = g.generate_raise(ctx) 148 | assert raise_ 149 | code = ast.unparse(raise_) 150 | assert compiles(code) 151 | 152 | 153 | def test_generate_global(ctx): 154 | global_ = g.generate_global(ctx) 155 | assert global_ 156 | code = ast.unparse(global_) 157 | assert compiles(code) 158 | 159 | 160 | def test_generate_nonlocal(ctx): 161 | nonlocal_ = g.generate_nonlocal(ctx) 162 | assert nonlocal_ 163 | code = ast.unparse(nonlocal_) 164 | assert compiles(code) 165 | 166 | 167 | def test_generate_for(ctx): 168 | ctx.max_depth = 1 169 | for_ = g.generate_for(ctx) 170 | assert for_ 171 | code = ast.unparse(for_) 172 | assert compiles(code) 173 | 174 | 175 | def test_generate_asyncfor(ctx): 176 | ctx.max_depth = 1 177 | asyncfor = g.generate_asyncfor(ctx) 178 | assert asyncfor 179 | code = ast.unparse(asyncfor) 180 | assert compiles(code) 181 | 182 | 183 | def test_generate_while(ctx): 184 | ctx.max_depth = 1 185 | while_ = g.generate_while(ctx) 186 | assert while_ 187 | code = ast.unparse(while_) 188 | assert compiles(code) 189 | 190 | 191 | def test_generate_if(ctx): 192 | ctx.max_depth = 1 193 | if_ = g.generate_if(ctx) 194 | assert if_ 195 | code = ast.unparse(if_) 196 | assert compiles(code) 197 | 198 | 199 | def test_generate_with(ctx): 200 | ctx.max_depth = 1 201 | with_ = g.generate_with(ctx) 202 | assert with_ 203 | code = ast.unparse(with_) 204 | assert compiles(code) 205 | 206 | 207 | def test_generate_asyncwith(ctx): 208 | ctx.max_depth = 1 209 | asyncwith = g.generate_asyncwith(ctx) 210 | assert asyncwith 211 | code = ast.unparse(asyncwith) 212 | assert compiles(code) 213 | 214 | 215 | def test_generate_assert(ctx): 216 | assert_ = g.generate_assert(ctx) 217 | assert assert_ 218 | code = ast.unparse(assert_) 219 | assert compiles(code) 220 | 221 | 222 | def test_generate_expression(ctx): 223 | expression = g.generate_expression(ctx) 224 | assert expression 225 | code = ast.unparse(expression) 226 | assert compiles(code) 227 | 228 | 229 | def test_generate_try(ctx): 230 | ctx.max_depth = 1 231 | try_ = g.generate_try(ctx) 232 | assert try_ 233 | code = ast.unparse(try_) 234 | assert compiles(code) 235 | 236 | 237 | def test_generate_trystar(ctx): 238 | ctx.max_depth = 1 239 | try_ = g.generate_trystar(ctx) 240 | assert try_ 241 | code = ast.unparse(try_) 242 | assert compiles(code) 243 | 244 | 245 | def test_generate_matchvalue(ctx): 246 | matchvalue = g.generate_matchvalue(ctx) 247 | assert matchvalue 248 | case = ast.unparse(matchvalue) 249 | code = f"match x:\n case {case}: pass" 250 | assert compiles(code) 251 | 252 | 253 | def test_generate_matchsingleton(ctx): 254 | singleton = g.generate_matchsingleton(ctx) 255 | assert singleton 256 | case = ast.unparse(singleton) 257 | code = f"match x:\n case {case}: pass" 258 | assert compiles(code) 259 | 260 | 261 | def test_generate_matchsequence(ctx): 262 | matchsequence = g.generate_matchsequence(ctx) 263 | assert matchsequence 264 | case = ast.unparse(matchsequence) 265 | code = f"match x:\n case {case}: pass" 266 | assert compiles(code) 267 | 268 | 269 | def test_generate_matchmapping(ctx): 270 | matchmapping = g.generate_matchmapping(ctx) 271 | assert matchmapping 272 | case = ast.unparse(matchmapping) 273 | code = f"match x:\n case {case}: pass" 274 | assert compiles(code) 275 | 276 | 277 | def test_generate_matchclass(ctx): 278 | matchclass = g.generate_matchclass(ctx) 279 | assert matchclass 280 | case = ast.unparse(matchclass) 281 | code = f"match x:\n case {case}: pass" 282 | assert compiles(code) 283 | 284 | 285 | def test_generate_matchas(ctx): 286 | matchas = g.generate_matchas(ctx) 287 | assert matchas 288 | case = ast.unparse(matchas) 289 | code = f"match x:\n case {case}: pass" 290 | assert compiles(code) 291 | 292 | 293 | def test_generate_matchor(ctx): 294 | matchor = g.generate_matchor(ctx) 295 | assert matchor 296 | case = ast.unparse(matchor) 297 | code = f"match x:\n case {case}: pass" 298 | assert compiles(code) 299 | 300 | 301 | def test_generate_match(ctx): 302 | match = g.generate_match(ctx) 303 | assert match 304 | code = ast.unparse(match) 305 | assert compiles(code), match 306 | 307 | 308 | def test_generate_list(ctx): 309 | list_ = g.generate_list(ctx) 310 | assert list_ 311 | code = ast.unparse(list_) 312 | assert compiles(code) 313 | 314 | 315 | def test_generate_tuple(ctx): 316 | tuple_ = g.generate_tuple(ctx) 317 | assert tuple_ 318 | code = ast.unparse(tuple_) 319 | assert compiles(code) 320 | 321 | 322 | def test_generate_boolop(ctx): 323 | boolop = g.generate_boolop(ctx) 324 | assert boolop 325 | code = ast.unparse(boolop) 326 | assert compiles(code) 327 | 328 | 329 | def test_generate_binop(ctx): 330 | binop = g.generate_binop(ctx) 331 | assert binop 332 | code = ast.unparse(binop) 333 | assert compiles(code) 334 | 335 | 336 | def test_generate_unaryop(ctx): 337 | unaryop = g.generate_unaryop(ctx) 338 | assert unaryop 339 | code = ast.unparse(unaryop) 340 | assert compiles(code) 341 | 342 | 343 | def test_generate_lambda(ctx): 344 | lambda_ = g.generate_lambda(ctx) 345 | assert lambda_ 346 | code = ast.unparse(lambda_) 347 | assert compiles(code) 348 | 349 | 350 | def test_generate_ifexp(ctx): 351 | ifexp = g.generate_ifexp(ctx) 352 | assert ifexp 353 | code = ast.unparse(ifexp) 354 | assert compiles(code) 355 | 356 | 357 | def test_generate_dict(ctx): 358 | dict_ = g.generate_dict(ctx) 359 | assert dict_ 360 | code = ast.unparse(dict_) 361 | assert compiles(code) 362 | 363 | 364 | def test_generate_set(ctx): 365 | set_ = g.generate_set(ctx) 366 | assert set_ 367 | code = ast.unparse(set_) 368 | assert compiles(code) 369 | 370 | 371 | def test_generate_listcomp(ctx): 372 | listcomp = g.generate_listcomp(ctx) 373 | assert listcomp 374 | code = ast.unparse(listcomp) 375 | assert compiles(code) 376 | 377 | 378 | def test_generate_setcomp(ctx): 379 | setcomp = g.generate_setcomp(ctx) 380 | assert setcomp 381 | code = ast.unparse(setcomp) 382 | assert compiles(code) 383 | 384 | 385 | def test_generate_dictcomp(ctx): 386 | dictcomp = g.generate_dictcomp(ctx) 387 | assert dictcomp 388 | code = ast.unparse(dictcomp) 389 | assert compiles(code) 390 | 391 | 392 | def test_generate_generatorexp(ctx): 393 | generatorexp = g.generate_generatorexp(ctx) 394 | assert generatorexp 395 | code = ast.unparse(generatorexp) 396 | assert compiles(code) 397 | 398 | 399 | def test_generate_await(ctx): 400 | await_ = g.generate_await(ctx) 401 | assert await_ 402 | code = ast.unparse(await_) 403 | assert compiles(code) 404 | 405 | 406 | def test_generate_yield(ctx): 407 | yield_ = g.generate_yield(ctx) 408 | assert yield_ 409 | code = ast.unparse(yield_) 410 | assert compiles(code) 411 | 412 | 413 | def test_generate_yieldfrom(ctx): 414 | yieldfrom = g.generate_yieldfrom(ctx) 415 | assert yieldfrom 416 | code = ast.unparse(yieldfrom) 417 | assert compiles(code) 418 | 419 | 420 | def test_generate_compare(ctx): 421 | compare = g.generate_compare(ctx) 422 | assert compare 423 | code = ast.unparse(compare) 424 | assert compiles(code) 425 | 426 | 427 | def test_generate_call(ctx): 428 | call = g.generate_call(ctx) 429 | assert call 430 | code = ast.unparse(call) 431 | assert compiles(code) 432 | 433 | 434 | def test_generate_formattedvalue(ctx): 435 | formattedvalue = g.generate_formattedvalue(ctx) 436 | assert formattedvalue 437 | code = ast.unparse(formattedvalue) 438 | assert code # isn't valid syntax 439 | 440 | 441 | def test_generate_joinedstr(ctx): 442 | joinedstr = g.generate_joinedstr(ctx) 443 | assert joinedstr 444 | code = ast.unparse(joinedstr) 445 | assert compiles(code) 446 | 447 | 448 | def test_generate_attribute(ctx): 449 | attribute = g.generate_attribute(ctx) 450 | assert attribute 451 | code = ast.unparse(attribute) 452 | assert compiles(code) 453 | 454 | 455 | def test_generate_subscript(ctx): 456 | subscript = g.generate_subscript(ctx) 457 | assert subscript 458 | code = ast.unparse(subscript) 459 | assert compiles(code) 460 | -------------------------------------------------------------------------------- /examples/output_default.py: -------------------------------------------------------------------------------- 1 | for e8m0xd0kbvx in {(yield from False): [e8m0xd0kbvx for sl6bqcxwh3g in 0j if '' if sl6bqcxwh3g if None], e8m0xd0kbvx.e8m0xd0kbvx: sl6bqcxwh3g[0.0:sl6bqcxwh3g:sl6bqcxwh3g]}: 2 | 3 | class adqa8j00i8q: 4 | assert 0, e8m0xd0kbvx 5 | from adqa8j00i8q import adqa8j00i8q, adqa8j00i8q as ozi1xls5irl, sl6bqcxwh3g 6 | break 7 | global ozi1xls5irl 8 | pass 9 | None.ozi1xls5irl: adqa8j00i8q = ozi1xls5irl 10 | ''.ozi1xls5irl *= sl6bqcxwh3g 11 | continue 12 | raise False 13 | n2rw5o0_ibu = b64jkd1qw0z = j56uuem5c4b = r4mfejds1d4 = stuupwa4r6p = j56uuem5c4b 14 | del ozi1xls5irl 15 | import j56uuem5c4b 16 | b'' in 0.0 17 | async for iss31fgiec6 in (owoqq2r81rf := ozi1xls5irl): 18 | from stuupwa4r6p import ozi1xls5irl, r4mfejds1d4 as e9u01zfk5lh, b64jkd1qw0z, adqa8j00i8q as d60cbfao387, r4mfejds1d4, ozi1xls5irl as o0nivw17yvy 19 | assert '' 20 | import iss31fgiec6 as wme4lewmk12, wme4lewmk12 as xjb71zv5fqg, o0nivw17yvy 21 | pass 22 | continue 23 | break 24 | r4mfejds1d4.owoqq2r81rf: n2rw5o0_ibu = sl6bqcxwh3g 25 | False 26 | raise None 27 | mwsdpq37crn = rff8otu0myw = q744lreapf_ = xzsg25pn5bw = igqsd94p2x4 = qkxleheyu_o = t8j4qkavho3 = gk8c_p6is18 = r4mfejds1d4 28 | else: 29 | del e8m0xd0kbvx 30 | global owoqq2r81rf 31 | 0 .owoqq2r81rf //= e8m0xd0kbvx 32 | 0j[0] += wme4lewmk12 33 | import qkxleheyu_o, stuupwa4r6p as vsrjv34ecrq, sl6bqcxwh3g as pwwfecvfukw, igqsd94p2x4, qkxleheyu_o, iss31fgiec6 as tvskrpouyiy, qkxleheyu_o, mwsdpq37crn as cbcyh1a0isw 34 | continue 35 | global r4mfejds1d4 36 | False 37 | pass 38 | break 39 | try: 40 | raise t8j4qkavho3 41 | m460w3d2sjm = '' 42 | e9u01zfk5lh[0j]: None = igqsd94p2x4 43 | assert owoqq2r81rf 44 | from adqa8j00i8q import owoqq2r81rf, tvskrpouyiy as tz5gipltlcg 45 | del tvskrpouyiy 46 | pass 47 | from sl6bqcxwh3g import adqa8j00i8q as wj64n383w03, pwwfecvfukw, b64jkd1qw0z, igqsd94p2x4 as m9x8d_brcb2, cbcyh1a0isw as v0ezxz7bq01, r4mfejds1d4 48 | b''.m9x8d_brcb2: wj64n383w03 = n2rw5o0_ibu 49 | global wme4lewmk12 50 | except* (yield 0.0) as d72g739amja: 51 | raise b'' 52 | sn3m3x92h8t = stuupwa4r6p 53 | rff8otu0myw.q744lreapf_ >>= '' 54 | del d72g739amja 55 | import tvskrpouyiy, pwwfecvfukw, vsrjv34ecrq as zg7yptztz5y, b64jkd1qw0z as yw5173o3n04, b64jkd1qw0z, qkxleheyu_o as oedi_4_u4mr, stuupwa4r6p, stuupwa4r6p 56 | assert None, v0ezxz7bq01 57 | continue 58 | False 59 | break 60 | import e8m0xd0kbvx, q744lreapf_ as ww77mwsdqe8, adqa8j00i8q as dp4ng79zg_x, n2rw5o0_ibu, vsrjv34ecrq as pha75rfng_a, xjb71zv5fqg, stuupwa4r6p, n2rw5o0_ibu as ff98c09yrgf, adqa8j00i8q as j0z0aikypoa 61 | match adqa8j00i8q(pha75rfng_a, 0.0, tz5gipltlcg, q744lreapf_=0, mwsdpq37crn=adqa8j00i8q, gk8c_p6is18=0j): 62 | case b'' if 0.0 % o0nivw17yvy: 63 | rc4ybrorlt3 = n1qjscf498h = tz5gipltlcg 64 | assert False, xzsg25pn5bw 65 | break 66 | del tvskrpouyiy 67 | from ww77mwsdqe8 import v0ezxz7bq01, rff8otu0myw as gq6a9efplsp, gk8c_p6is18, d60cbfao387 as x6e7chjmif5, dp4ng79zg_x, oedi_4_u4mr as zqcxv3pnr7n, wj64n383w03, m460w3d2sjm as v9r2xh2c74_, ww77mwsdqe8 68 | pass 69 | continue 70 | xkcw7273it4: '' = qkxleheyu_o 71 | raise 0j 72 | n2rw5o0_ibu[0] &= None 73 | case {0j: None, '': b'', **q744lreapf_}: 74 | ff98c09yrgf 75 | global o0nivw17yvy 76 | pass 77 | del tz5gipltlcg 78 | raise 0 79 | import ff98c09yrgf, sl6bqcxwh3g as eer2_awzvxs 80 | break 81 | assert d60cbfao387, n2rw5o0_ibu 82 | e773lvh7ye9 = s2wskspqubw = 0.0 83 | (ghx6vhg091d): None = n2rw5o0_ibu 84 | continue 85 | with False: 86 | 0.0 87 | mf2g99q33my ^= ozi1xls5irl 88 | from wme4lewmk12 import d60cbfao387, v9r2xh2c74_ as zq5vxx9b3ef, mwsdpq37crn as wsookixdihh, wsookixdihh, e8m0xd0kbvx as dx3qhmw8one, v0ezxz7bq01, sn3m3x92h8t as q33crm4ctrs 89 | global rc4ybrorlt3 90 | 0 .sl6bqcxwh3g: False = ww77mwsdqe8 91 | from m9x8d_brcb2 import zqcxv3pnr7n as v9tyf5oxpmq, j0z0aikypoa, tz5gipltlcg as zky2nv7x_3g, wme4lewmk12 92 | continue 93 | import b64jkd1qw0z, b64jkd1qw0z as w7kkz6n_ty7, xjb71zv5fqg as ekluthtvudg, v9tyf5oxpmq, r4mfejds1d4, w7kkz6n_ty7 as kikjuv3g1v6, zg7yptztz5y 94 | break 95 | pass 96 | with [w7kkz6n_ty7, b64jkd1qw0z, b'', '', mf2g99q33my], {qkxleheyu_o for fsubws9zk27 in None if 0j} as mrmy6h1mwaz: 97 | iss31fgiec6 98 | assert ozi1xls5irl 99 | global xkcw7273it4 100 | del gq6a9efplsp 101 | raise None 102 | m6vsa648znz = tvskrpouyiy 103 | vqv5y1n4o7e -= 0j 104 | q33crm4ctrs 105 | del ghx6vhg091d 106 | break 107 | (lambda nb7nlr22eru, jgv773b368g: ('', 0.0, eer2_awzvxs)).o0nivw17yvy: (q33crm4ctrs for y5nv91qitlf in qkxleheyu_o if {False: b'' for k5r_l841o7v in sn3m3x92h8t if r4mfejds1d4} if f'wzv2t3ch87q{0}gmvp78fierq{False}s6k508db933{s6k508db933}{fsubws9zk27}l5yi4_su1e5y4223s_784ic0mbb1xpgvy' if (b'' if 0.0 else sl6bqcxwh3g) for l2icuz3bh1v in adqa8j00i8q.r4mfejds1d4) = wj64n383w03 108 | while {None if 0 else igqsd94p2x4 for sj4duil0hib in {e9u01zfk5lh: rff8otu0myw, 0j: zg7yptztz5y, '': 0}}: 109 | assert ('' for a24tg2x2w7g in tvskrpouyiy if None) 110 | 111 | class dm9hs0exqh8: 112 | from xkcw7273it4 import dp4ng79zg_x, b64jkd1qw0z, yw5173o3n04 as pww1c1m6210, owoqq2r81rf as s836e6eo88x, y5nv91qitlf 113 | continue 114 | pass 115 | dghae4ztn5b = s836e6eo88x 116 | raise 0.0 117 | global v9tyf5oxpmq 118 | zky2nv7x_3g.rc4ybrorlt3 |= y5nv91qitlf 119 | import pwwfecvfukw, zqcxv3pnr7n as mxpnplwy4ir, m6vsa648znz, r4mfejds1d4 as dqbq7bt3s0p, s2wskspqubw, rc4ybrorlt3 as o9xfyjs8l_u, m6vsa648znz, k5r_l841o7v, vqv5y1n4o7e as qoqv83j3lvl 120 | import v0ezxz7bq01 as ry4u18ffe7s, tz5gipltlcg, t8j4qkavho3 121 | 0j 122 | from dm9hs0exqh8 import nb7nlr22eru, q744lreapf_ as c4f_kxa8jxl, dp4ng79zg_x, w7kkz6n_ty7 as ytvellnkm_b, stuupwa4r6p, sj4duil0hib as lw85fzg4qvi 123 | for st10j7i65fy in (yield from b''): 124 | ekluthtvudg.l5yi4_su1e5: False = rc4ybrorlt3 125 | pcvm5gyaemt **= iss31fgiec6 126 | pass 127 | raise 0 128 | global ytvellnkm_b 129 | dx1_yj_poj_ = v9r2xh2c74_ 130 | assert 0.0 131 | break 132 | continue 133 | del l2icuz3bh1v 134 | else: 135 | continue 136 | break 137 | raise y5nv91qitlf 138 | import gmvp78fierq, sj4duil0hib as y3to7lx4rb1, pcvm5gyaemt, ytvellnkm_b as hv2v5ub0krr, o9xfyjs8l_u, mf2g99q33my, ekluthtvudg as hiknzgdzbiw, zq5vxx9b3ef as u1ednx0raid, pww1c1m6210 139 | False .c4f_kxa8jxl <<= dghae4ztn5b 140 | 0j 141 | (gv9zs51nck7): b'' = d60cbfao387 142 | swwsouxrfbj = hujg90i38hk = xna2g_rdia8 = wsbq1685t7w = rx0w_4k1lj8 = psm8ns1hl2c = f5rp768g0q4 = e0s_yjy_89x = l7k_5zqok75 = sib2opklrs8 = None 143 | pass 144 | del dqbq7bt3s0p 145 | assert hujg90i38hk('', o0nivw17yvy, c4f_kxa8jxl=None), await pww1c1m6210 146 | global dx1_yj_poj_ 147 | with (yield gq6a9efplsp): 148 | from w7kkz6n_ty7 import n2rw5o0_ibu as cpf7bxjgktk 149 | global fsubws9zk27 150 | uithkt3896m %= 0.0 151 | import jgv773b368g, t8j4qkavho3, v9r2xh2c74_ as ev1vadvc7uv, iss31fgiec6 as h9563e13uco, gv9zs51nck7 152 | continue 153 | from k5r_l841o7v import qkxleheyu_o, wzv2t3ch87q as bjfdl3t0u5v, psm8ns1hl2c as wupxpybil0g, sib2opklrs8, gk8c_p6is18, dm9hs0exqh8 as qhqyds4gf65, dghae4ztn5b 154 | t3c3yl4x53f = lw85fzg4qvi 155 | del r4mfejds1d4 156 | assert 0 157 | pass 158 | (dwa6kpw6ony): sj4duil0hib[''] = xkcw7273it4 // False 159 | while b'': 160 | 0j 161 | break 162 | raise kikjuv3g1v6 163 | from vsrjv34ecrq import mxpnplwy4ir, bjfdl3t0u5v as kkv6rxik4wq, wsbq1685t7w, l5yi4_su1e5 as jh963tkphph 164 | fhw0kyw83xg = a0b7ip50k79 = lirpo3vz3x8 = None 165 | 0.0 166 | raise c4f_kxa8jxl 167 | import m460w3d2sjm as mukqj25ywy9, zqcxv3pnr7n as jwpik32rz1f, psm8ns1hl2c, s2wskspqubw, pwwfecvfukw as egxsrew7fp2, hv2v5ub0krr as mp6t75_at61, igqsd94p2x4, mxpnplwy4ir as x5qztb9ec5i, st10j7i65fy, gmvp78fierq 168 | (nt4o5389vih): 0 = ytvellnkm_b 169 | assert l5yi4_su1e5, b'' 170 | else: 171 | False .a0b7ip50k79 @= gmvp78fierq 172 | pass 173 | del s6k508db933 174 | continue 175 | break 176 | global dp4ng79zg_x 177 | from xkcw7273it4 import pcvm5gyaemt, zg7yptztz5y, sl6bqcxwh3g as hv88r6ypand, j0z0aikypoa, hv2v5ub0krr as aokhvgcll8s, qhqyds4gf65 as s5iyymimp6a 178 | global e8m0xd0kbvx 179 | raise d60cbfao387 180 | break 181 | jwpik32rz1f 182 | b8sn2os54yz = {{0j, e9u01zfk5lh, '', 0, o0nivw17yvy, '', d72g739amja, yw5173o3n04, b'', False}: (hujg90i38hk, 0.0, x5qztb9ec5i, d60cbfao387, None, 0j) for r9t0xlzboh8 in (ljhq7p2ti1s := d72g739amja) if b'' for kfz8ug_e_xj in f'{gk8c_p6is18}nxvj2gvsudm{0.0}cayliek309v{ekluthtvudg}{0j}{jgv773b368g}{uithkt3896m}ztb3ss70hc0{None}' if [0 for x3k9ecx3jgs in x6e7chjmif5 if False] if ww77mwsdqe8 not in ''} 183 | for yhgs9h8ngy9 in {[oedi_4_u4mr, 0, zky2nv7x_3g, hiknzgdzbiw, 0j, None]}: 184 | with lambda l_3g7_f1jcl, tmi5cqn3nxy, cqqycrk921z, z2jzrg0z7pq, cfdxooipr_s, uezho48j317, flfzmjvwqhk: cpf7bxjgktk as aj1zhuytprt, ~z2jzrg0z7pq as dnw4nenn41g, (b'', owoqq2r81rf, False, 0.0, dghae4ztn5b, pwwfecvfukw, '', 0, gv9zs51nck7, b''): 185 | assert owoqq2r81rf, ev1vadvc7uv 186 | import tz5gipltlcg 187 | ''.iss31fgiec6 /= None 188 | del sib2opklrs8 189 | pass 190 | (kx6vtv2it7q): rc4ybrorlt3 = eer2_awzvxs 191 | del r9t0xlzboh8 192 | from a24tg2x2w7g import l5yi4_su1e5 as kwm1wbdmfos, jwpik32rz1f 193 | helkt8hptfg -= False 194 | continue 195 | assert lambda e0lu_8k7t9v, t_5yi76od_1, zo2vy1eu_vc: ghx6vhg091d 196 | async for g5bpo2t0793 in [0.0, d72g739amja]: 197 | raise 0j 198 | osbia2zt1tp = hv2v5ub0krr 199 | import wsookixdihh 200 | break 201 | global n1qjscf498h 202 | (z9mthql_7ny): 0.0 = False 203 | j0z0aikypoa 204 | pass 205 | ''.x3k9ecx3jgs: sl6bqcxwh3g = m6vsa648znz 206 | del u1ednx0raid 207 | global s5iyymimp6a 208 | continue 209 | (yield from None).aokhvgcll8s <<= await oedi_4_u4mr 210 | from kx6vtv2it7q import wzv2t3ch87q as pboghp36fx0, a24tg2x2w7g, g5bpo2t0793 211 | ymz7rw8oqhn = {b'' for t2tikye27jw in cayliek309v if 0j if z9mthql_7ny if lw85fzg4qvi} 212 | assert [0 for ji3uwn0uayd in q744lreapf_ for s10k447gwe_ in b'' if False if v9tyf5oxpmq if zq5vxx9b3ef] 213 | if 0: 214 | 0j 215 | raise None 216 | pass 217 | import uezho48j317 as dqsh5wh2w92, s6k508db933 as rmcf5npf95z, l2icuz3bh1v, tmi5cqn3nxy as tgk3bopwomh, swwsouxrfbj, l5yi4_su1e5 as o4qwvm7xnqd, m6vsa648znz 218 | break 219 | continue 220 | pass 221 | from kx6vtv2it7q import pcvm5gyaemt, wme4lewmk12 as ch_ph3gagrz, rc4ybrorlt3, x6e7chjmif5 as groitmuc6w9, x6e7chjmif5 as q60gbbtrht6, ev1vadvc7uv, hv88r6ypand as g2qrqvggi9e 222 | del tmi5cqn3nxy 223 | break 224 | with {e0lu_8k7t9v, yhgs9h8ngy9, '', 0.0, c4f_kxa8jxl, '', q744lreapf_, groitmuc6w9, 0j, nt4o5389vih}(not b'', t_5yi76od_1=False .wzv2t3ch87q, b8sn2os54yz=o9xfyjs8l_u > z9mthql_7ny, e9u01zfk5lh=wj64n383w03[None]) as c6oljvl29_x, (flfzmjvwqhk for fg9boku0oap in 0.0 if ozi1xls5irl for jmaofb1cjl9 in False) / (cayliek309v if 0j else b''): 225 | {vqv5y1n4o7e}.fg9boku0oap ^= mukqj25ywy9 226 | global dm9hs0exqh8 227 | match (yield ''): 228 | case False if {q33crm4ctrs: None}: 229 | 0j 230 | import gv9zs51nck7, rmcf5npf95z as j64o8_sphkf, stuupwa4r6p 231 | raise t_5yi76od_1 232 | assert 0, rx0w_4k1lj8 233 | wme4lewmk12[False]: flfzmjvwqhk = '' 234 | zk22f4ild08 = 0.0 235 | import zo2vy1eu_vc 236 | from zqcxv3pnr7n import t8j4qkavho3 as s06ng4eeiia, rx0w_4k1lj8, n1qjscf498h as u4jg0nnhs6m, kx6vtv2it7q as ignfn6bnst_, t2tikye27jw, y5nv91qitlf 237 | (thxobe51di7): b'' = dwa6kpw6ony 238 | kxl68_098sk = psm8ns1hl2c 239 | match dwa6kpw6ony: 240 | case bha8g3_q7y9 if (w1_juwcd_wm := 0): 241 | global m9x8d_brcb2 242 | del dghae4ztn5b 243 | ry4u18ffe7s[0j] &= None 244 | b'' 245 | assert cpf7bxjgktk, pwwfecvfukw 246 | pass 247 | raise False 248 | dx1_yj_poj_[0.0]: '' = j56uuem5c4b 249 | xkcw7273it4 250 | pass 251 | 252 | @mp6t75_at61 if 0j else None 253 | @(jyy35lg9r5_ := eer2_awzvxs) 254 | @False(j0z0aikypoa, b'', g2qrqvggi9e, hiknzgdzbiw='', z9mthql_7ny=0.0, em8qm503o0w=cayliek309v) 255 | @{0} 256 | @(yield vqv5y1n4o7e) 257 | @zg7yptztz5y 258 | class t58di28mkkk(f'em8qm503o0wm1a1kz6515s{None}vurmq85cyhezdpxospod1x{0j}{m6vsa648znz}bu99miw2zte{j56uuem5c4b}{False}', {0: jwpik32rz1f for s8ah057o8hr in qkxleheyu_o if '' if b'' if fsubws9zk27 for jdxe_2jvsd1 in 0.0 for c2l8a3szw_9 in groitmuc6w9 if None if tvskrpouyiy if ji3uwn0uayd}, 0.0 < False == a0b7ip50k79): 259 | import w1_juwcd_wm, l2icuz3bh1v as gtu94sj313x 260 | assert x3k9ecx3jgs 261 | global sib2opklrs8 262 | del u1ednx0raid 263 | jciuc0h7qya = b'' 264 | raise 0 265 | e0lu_8k7t9v.pwwfecvfukw |= x3k9ecx3jgs 266 | from mp6t75_at61 import e0lu_8k7t9v as s8ibu2s3prk, cqqycrk921z, c6oljvl29_x as gkyx45tyxi6, g5bpo2t0793 267 | assert '' 268 | m0io2dsoeoi: zky2nv7x_3g = hv2v5ub0krr 269 | 270 | class f2v6r68rklf({0j, f2v6r68rklf, 0, e0lu_8k7t9v}, '', rx0w_4k1lj8): 271 | r6e1nq409ey = False 272 | vurmq85cyhe 273 | global bjfdl3t0u5v 274 | pass 275 | lhny2o8uyrs @= None 276 | import u4jg0nnhs6m, n1qjscf498h, nb7nlr22eru as lgn4im7x65a, e9u01zfk5lh, zdpxospod1x as ts8dqxo37i8, n2rw5o0_ibu, s5iyymimp6a as ewol2c5jo55, sl6bqcxwh3g as ledizl_ecv3, u1ednx0raid 277 | from zqcxv3pnr7n import adqa8j00i8q 278 | del dx1_yj_poj_ 279 | raise k5r_l841o7v 280 | del ch_ph3gagrz 281 | h506z90o968 += {0.0: 0j} 282 | for lr5f6wu2k_6 in -t58di28mkkk: 283 | from l_3g7_f1jcl import c6oljvl29_x, j64o8_sphkf, stuupwa4r6p as fzjj34_390n, yw5173o3n04 as w82zq6nhxv4, q33crm4ctrs 284 | pass 285 | '' 286 | evx_d5b0upd = jzna3_5nexa = jf2t2ykjl32 = n0yx2gqdt0y = csabf1r0pqq = w9cb50eeshi = i8yx3qiq8gg = gicr4f60ai7 = g6320bhpger = nig5sqvlhha = None 287 | assert ch_ph3gagrz, b'' 288 | break 289 | (q57oo23qalq): 0.0 = zdpxospod1x 290 | global e9u01zfk5lh 291 | import cqqycrk921z as khu6_hi8ui8, vqv5y1n4o7e, ztb3ss70hc0, q57oo23qalq as wtg5ni43ls8, l_3g7_f1jcl 292 | raise n1qjscf498h 293 | else: 294 | pass 295 | vsrjv34ecrq 296 | global ljhq7p2ti1s 297 | raise 0 298 | import vqv5y1n4o7e as gcmn39yo8c0, cbcyh1a0isw, ghx6vhg091d as ooqbkn19df0, dx1_yj_poj_ 299 | assert 0 300 | (lew29ewqq62): 0j = ry4u18ffe7s 301 | del zqcxv3pnr7n 302 | pyq0_q9th5g = ewol2c5jo55 303 | from stuupwa4r6p import vqv5y1n4o7e, dghae4ztn5b as f8zpwz5qc54 304 | try: 305 | vor7a1d0h_6 >>= mxpnplwy4ir 306 | global gicr4f60ai7 307 | pass 308 | del g5bpo2t0793 309 | ''.groitmuc6w9 **= t_5yi76od_1 310 | raise None 311 | import jh963tkphph, lw85fzg4qvi, zg7yptztz5y as losh5rbax00, uezho48j317, ljhq7p2ti1s as lxdj66958uh, g5bpo2t0793 as flexl9omwj0, jyy35lg9r5_ 312 | wdvk4ighadb = mvu_wx3or1w = p2wmxiamgs2 = lhxj1yvibmp = vlknyfvmyls = naujsum571b = nomlxpc3hlv = adqjyul1tmb = False 313 | zq5vxx9b3ef 314 | (h13pq_zaurf): r9t0xlzboh8 = b'' 315 | except {s6k508db933: 0.0 for ps9d215n001 in b'' if dwa6kpw6ony if nomlxpc3hlv if 0j} as as5klbea4uz: 316 | from lhxj1yvibmp import gmvp78fierq, ytvellnkm_b as j4kkturivgi, owoqq2r81rf as cf5qxvdl629 317 | assert None 318 | pass 319 | assert dqbq7bt3s0p, '' 320 | qoqv83j3lvl 321 | import dp4ng79zg_x, tvskrpouyiy, kx6vtv2it7q as d0ujnhwvs2w, jzna3_5nexa 322 | from z9mthql_7ny import s8ibu2s3prk as fdub4b1a3p_, zo2vy1eu_vc as bnc33fegodk, y4223s_784i, g2qrqvggi9e, fhw0kyw83xg as wzipmh_us4k, gkyx45tyxi6 as okaaa53ktzf, qkxleheyu_o 323 | del w9cb50eeshi 324 | iz4h1nzg5x8 *= fdub4b1a3p_ 325 | global gq6a9efplsp 326 | except [0.0, False, w1_juwcd_wm, hv2v5ub0krr, 0, cpf7bxjgktk, 0, False]: 327 | wk_95owxlkm = wme4lewmk12 328 | raise nt4o5389vih 329 | 0.0.o9xfyjs8l_u: '' = q57oo23qalq 330 | global o4qwvm7xnqd 331 | eer2_awzvxs 332 | pass 333 | del s2wskspqubw 334 | 0j[b''] //= stuupwa4r6p 335 | dl4q1ccr0o7 = gq6a9efplsp 336 | from o9xfyjs8l_u import y5nv91qitlf, nxvj2gvsudm, tvskrpouyiy as k8m6ermkqz_, ch_ph3gagrz as pvwqup5iqbq, f5rp768g0q4 337 | else: 338 | None[None]: rmcf5npf95z = False 339 | import vurmq85cyhe as u0px4icce0p, pcvm5gyaemt, j64o8_sphkf as kt7jfh9rl15, jdxe_2jvsd1 as m8x21lligl6 340 | raise '' 341 | assert aokhvgcll8s 342 | pass 343 | raise rx0w_4k1lj8 344 | ym7z7qw022s /= b'' 345 | del rff8otu0myw 346 | import hiknzgdzbiw, psm8ns1hl2c as n2oigs1v6qt, cfdxooipr_s, wk_95owxlkm, iz4h1nzg5x8 as rwwr5cbnq9n, ozi1xls5irl as uw9r_v0r6ta, cqqycrk921z, s2wskspqubw as qh_cmqbmoyy, aokhvgcll8s 347 | xawp6dtqrcy = 0j 348 | assert (u0px4icce0p, 0.0, cbcyh1a0isw, w1_juwcd_wm, 0, None, s8ah057o8hr, b'', e9u01zfk5lh, q57oo23qalq) 349 | async with lambda mietjgxusai, rvfeu_2_82m, r12we63jiqz: 0j[False], {kx6vtv2it7q for iigxzj3c3j5 in 0.0 if vqv5y1n4o7e if 0 if '' for sf4mky85ouq in rx0w_4k1lj8 if nxvj2gvsudm for c_6lxxujv6q in 0j if mukqj25ywy9 if b''}.thxobe51di7: 350 | try: 351 | hoievan1rvn: '' = psm8ns1hl2c 352 | global ts8dqxo37i8 353 | d60cbfao387 354 | from wj64n383w03 import rx0w_4k1lj8, m9x8d_brcb2 as w9ve8d1zm3_, rc4ybrorlt3, hoievan1rvn, s2wskspqubw as ccezh1ewx1i, pww1c1m6210 as bylne2m33ud, ts8dqxo37i8, xawp6dtqrcy as z47s9pyfwxh, as5klbea4uz, jmaofb1cjl9 355 | raise None 356 | assert xkcw7273it4, 0 357 | v0ezxz7bq01 358 | mz_g9tyak26 %= y4223s_784i 359 | global sj4duil0hib 360 | pass 361 | except await 0.0: 362 | import sj4duil0hib, jgv773b368g as nux_x4e7jps 363 | xpjutbj8ky9 = False 364 | from s836e6eo88x import dwa6kpw6ony, s8ibu2s3prk as bqj84_qdnnt, gq6a9efplsp, bylne2m33ud as ax3jnwv3d42, lr5f6wu2k_6, jwpik32rz1f, bylne2m33ud as zf_gk2c62ye 365 | v9tyf5oxpmq.egxsrew7fp2: b'' = aokhvgcll8s 366 | del ji3uwn0uayd 367 | from cbcyh1a0isw import s10k447gwe_, tgk3bopwomh, jdxe_2jvsd1 as cbvazddwdpt, dx3qhmw8one as jjveg6uz3ky, ljhq7p2ti1s, gcmn39yo8c0, mwsdpq37crn as agvyrf6z4we, lirpo3vz3x8 368 | del pvwqup5iqbq 369 | assert qoqv83j3lvl, 0 370 | pass 371 | global a0b7ip50k79 372 | finally: 373 | d72g739amja 374 | raise ry4u18ffe7s 375 | import hv2v5ub0krr, ccezh1ewx1i as hy_pfrrplcz, p2wmxiamgs2, ts8dqxo37i8 as fi7b2s1rs7s, egxsrew7fp2, cbvazddwdpt as ay82xr1ukbs, gmvp78fierq as je3yzh0hs1t, qkxleheyu_o 376 | z9s97pgxohk = yqalyvt87iv = 0.0 377 | uetikbg_mvi //= '' 378 | ccezh1ewx1i.qhqyds4gf65: r12we63jiqz = m460w3d2sjm 379 | raise None 380 | False 381 | btxlwhsu2jd = ryr2d9v8owl = be5n4ptiseo = xqnai0onv3l = aj88o8hol1s = zo2vy1eu_vc 382 | zg7yptztz5y.nux_x4e7jps *= 0j 383 | import g5bpo2t0793, wsookixdihh as ga70jaesyro, b8sn2os54yz, nomlxpc3hlv as xyzvn1fotdf, l2icuz3bh1v, ledizl_ecv3 as o5il0klocll, hujg90i38hk as b90km2c52df 384 | with [0.0 for n5zanflvaf5 in fi7b2s1rs7s if iss31fgiec6 if False], f'{''}wu9_9amkasf{e8m0xd0kbvx}h7jih5rzrznej7v2_tinc2{b''}{agvyrf6z4we}{j0z0aikypoa}{0}{rmcf5npf95z}' as omrefdyzdpn, (yield from None): 385 | from c2l8a3szw_9 import tmi5cqn3nxy, mvu_wx3or1w as he5z5ps3iqv, zdpxospod1x, lgn4im7x65a as tv4ax2ye8i7, a0b7ip50k79 as wg7iglvw1j3 386 | del mxpnplwy4ir 387 | assert ps9d215n001 388 | pass 389 | 0j.dqsh5wh2w92: ch_ph3gagrz = nxvj2gvsudm 390 | global omrefdyzdpn 391 | pass 392 | 0.0 393 | import l5yi4_su1e5, vor7a1d0h_6 as fp0atc_2_12, iigxzj3c3j5, losh5rbax00, adqa8j00i8q as vxm4mw69j33, ay82xr1ukbs as x0utw0pi62q, c4f_kxa8jxl, he5z5ps3iqv 394 | m0z88tho37_ = False 395 | (igqsd94p2x4 for p17zx55j3yy in 0 if ljhq7p2ti1s for rgv4e8xcvmf in 0j).qh_cmqbmoyy /= helkt8hptfg ^ hiknzgdzbiw 396 | del rvfeu_2_82m 397 | if {'', None, n5zanflvaf5}: 398 | assert flfzmjvwqhk 399 | from aokhvgcll8s import ztb3ss70hc0 as f5htjbapwee, hv2v5ub0krr, gv9zs51nck7 as rplg3ic2vo9, fg9boku0oap as lnleyjsfg3g, cfdxooipr_s, e9u01zfk5lh, v9tyf5oxpmq as rnctcq4u4t5 400 | raise b'' 401 | global rvfeu_2_82m 402 | (hb3416l1uc0): c4f_kxa8jxl = b'' 403 | import k5r_l841o7v as z7gbbthiyfu, sib2opklrs8, qhqyds4gf65 as vmry3gmsxsk, uw9r_v0r6ta, mietjgxusai as tg3cg7nfh1i, cbcyh1a0isw, n2oigs1v6qt, s836e6eo88x as pvtmoru5x3s, n2oigs1v6qt as puep3tko1ep, sl6bqcxwh3g 404 | del qhqyds4gf65 405 | raise False 406 | pass 407 | global yw5173o3n04 408 | match 0j if d72g739amja else khu6_hi8ui8: 409 | case 0.0 | l1c1dwf0lt7 | wzg002nbh97.llxo6yxsvre if None: 410 | (chvnwwps9ry): as5klbea4uz = 0 411 | y_kdr8nhjnl = tvskrpouyiy 412 | from l5yi4_su1e5 import wj64n383w03, r4mfejds1d4, j0z0aikypoa as jntzp0qf0so, zk22f4ild08, hb3416l1uc0 as avaqs699fkt, iigxzj3c3j5 as xdh4mru7so5, u1ednx0raid 413 | '' 414 | rx0w_4k1lj8.d0ujnhwvs2w **= '' 415 | assert i8yx3qiq8gg, b'' 416 | del cayliek309v 417 | import wtg5ni43ls8, hb3416l1uc0, stuupwa4r6p as vdmqhpnx4ie, cfdxooipr_s as vs66sbzf4n4, iigxzj3c3j5 418 | assert None 419 | qgwuyg8n67b = s8ah057o8hr 420 | 421 | @b'' >> lnleyjsfg3g 422 | @None[fg9boku0oap:False:n1qjscf498h] 423 | def zo9kah0g57e(c6tv6ua8z3f: wtg5ni43ls8, st9ahnanzpu, owjffj6epvx, socjywcem1l: ryr2d9v8owl, zj578lc2jqb: wk_95owxlkm, wsawiqop4ez, fhzeoq9rf_k: mf2g99q33my, cj1p4htdhcy, a96bl_c6wcq: cj1p4htdhcy): 424 | raise hv2v5ub0krr 425 | (m0xjk6tf5zu): 0.0 = 0 426 | g2qrqvggi9e 427 | from mvu_wx3or1w import flfzmjvwqhk as zf6iza57y1g, ignfn6bnst_, n2oigs1v6qt as m579ta6728h 428 | global b90km2c52df 429 | pass 430 | lnleyjsfg3g[False] >>= 0j 431 | return ax3jnwv3d42 432 | pass 433 | del t2tikye27jw 434 | try: 435 | global lhxj1yvibmp 436 | zdyp3gncot4: 0j = psm8ns1hl2c 437 | fdub4b1a3p_[''] &= 0.0 438 | cayliek309v 439 | raise 0 440 | from r9t0xlzboh8 import ignfn6bnst_, zf6iza57y1g as rsm2yj5y4_9 441 | import avaqs699fkt, zo2vy1eu_vc as thmdb5mj_da, adqjyul1tmb as lj17b9hev75, vs66sbzf4n4, a96bl_c6wcq, owjffj6epvx as ls4nsh52c52 442 | assert 0.0, llxo6yxsvre 443 | nsw09zjry5h = tjtswqplthe = bm7wm41vrq2 = ai598802x1t = qhsar04g9h8 = wr5dt4ku6x9 = wr5dt4ku6x9 444 | raise 0j 445 | except await vdmqhpnx4ie as q4_e519i70t: 446 | assert 0 447 | import gkyx45tyxi6, q744lreapf_ as zfjfh3usxqu, gq6a9efplsp as qre0e1c3i9j, ts8dqxo37i8, cj1p4htdhcy as pcdntz__1_l, uw9r_v0r6ta, g5bpo2t0793 as gxuv125em_z, aj1zhuytprt, dwa6kpw6ony 448 | q4k8eu5nn4r ^= '' 449 | del groitmuc6w9 450 | global jgv773b368g 451 | kt7jfh9rl15.pcvm5gyaemt: False = zdpxospod1x 452 | pass 453 | p850prhfubm = wsbq1685t7w 454 | None 455 | from nux_x4e7jps import osbia2zt1tp, gmvp78fierq as t37hfzpmiqv, t8j4qkavho3, f5rp768g0q4 as e3r1e7txrjq, bjfdl3t0u5v as sypvelvakx4, n2oigs1v6qt, vxm4mw69j33 as aln2eghvzf8, dp4ng79zg_x, a24tg2x2w7g as xgzlf1uffgu 456 | finally: 457 | assert k5r_l841o7v 458 | pfkzec7x4d_ = b'' 459 | False[0.0] @= hoievan1rvn 460 | from oedi_4_u4mr import c6tv6ua8z3f as v7s8uk70h9f, f8zpwz5qc54 461 | 0 .s6k508db933: dx3qhmw8one = avaqs699fkt 462 | '' 463 | del wupxpybil0g 464 | global g2qrqvggi9e 465 | pass 466 | raise None 467 | import ozi1xls5irl as wa7yfltjtcp, n2rw5o0_ibu, xna2g_rdia8 as t8lmp9ih9xu 468 | with {zo2vy1eu_vc: ledizl_ecv3 for j9zfoe0gb93 in 0j if b'' if u0px4icce0p if False} <= (lambda l_qm7dbwwc5, dqrgwdpg4aw, nqbihhb3b23, vz_bi45n4q6, hg5lum7lmc7, ku5hb5f4j28, ggz5v81oazr, hvuxjelvfb2, gk7u3usy9vs: d72g739amja) >= (o9xfyjs8l_u, None, 0, mf2g99q33my) as oetmtusvms3: 469 | 470 | class vn1c6qfsxyu: 471 | pass 472 | '' 473 | raise mxpnplwy4ir 474 | import jh963tkphph as g01_ihf0xse, xpjutbj8ky9 as cwucxs1jbfz, qre0e1c3i9j, osbia2zt1tp as yzck6f9tqcz, xawp6dtqrcy, w1_juwcd_wm 475 | al9c66wdt14 -= b'' 476 | del je3yzh0hs1t 477 | xs9_w76qgdc = kthv1mgc4iz = f8iwq4kicwb = k01t2b879o1 = d59j77cl9g3 = pr6x3k23055 = rolc6apto2g = ga70jaesyro 478 | (zzk0sgv0zb7): l7k_5zqok75 = 0j 479 | assert g5bpo2t0793, 0.0 480 | from mxpnplwy4ir import fi7b2s1rs7s as byk_7xe6d1u, qre0e1c3i9j, s06ng4eeiia, dwa6kpw6ony as ddpcw6t_fjv, je3yzh0hs1t 481 | match (n1qjscf498h for qmw2e_w2mvx in False for spp1q494kzg in 0.0): 482 | case [0, True, None, *_]: 483 | global t2tikye27jw 484 | pass 485 | from hiknzgdzbiw import wtg5ni43ls8 as j0gn61f1efk, x6e7chjmif5 as ap228ed0_k6, cj1p4htdhcy, al9c66wdt14 as mgkkethw9w0, osbia2zt1tp, ledizl_ecv3 as vukel6iovq9, n0yx2gqdt0y, agvyrf6z4we 486 | assert m1a1kz6515s, None 487 | je76cjnyb99 = q4_e519i70t 488 | raise b'' 489 | rgv4e8xcvmf.lj17b9hev75: f5htjbapwee = 0j 490 | import m8x21lligl6, y5nv91qitlf as iaykpjo_2yl, lhxj1yvibmp as v78vdlvb5ij, jwpik32rz1f, egxsrew7fp2, vn1c6qfsxyu as j90l2c9ddzo 491 | v9r2xh2c74_ 492 | ''.ewol2c5jo55 |= t2tikye27jw 493 | with ''.yhgs9h8ngy9 as l46c9tw2zn7, [qmw2e_w2mvx for p7p54c601b2 in b'' if lj17b9hev75 if 0.0 for gra5wh53k22 in ts8dqxo37i8 if False]: 494 | global v9tyf5oxpmq 495 | del zf_gk2c62ye 496 | (cyhyt_ylpoj): zq5vxx9b3ef = None 497 | from q33crm4ctrs import q60gbbtrht6 as g764s94xcg2, rplg3ic2vo9, z9mthql_7ny, l5yi4_su1e5 as li_gja9_ht9, adqjyul1tmb, mgkkethw9w0 as rnusr7j4tai, r6e1nq409ey, r4mfejds1d4 as mijdzvh56ml 498 | del thxobe51di7 499 | pass 500 | raise 0j 501 | import x3k9ecx3jgs, pyq0_q9th5g as voueiuslcsg, t2tikye27jw, kt7jfh9rl15 as t1bvna2zq92, nux_x4e7jps, h506z90o968 as po8uoqdpk7h 502 | r9t0xlzboh8.cwucxs1jbfz += ai598802x1t 503 | vt7akg7mxt7 = 0 504 | yield from b'' 505 | global gq6a9efplsp 506 | assert [m9x8d_brcb2, n0yx2gqdt0y, False, jf2t2ykjl32, None, m460w3d2sjm, 0.0, sypvelvakx4] 507 | del rx0w_4k1lj8 508 | pass 509 | if (us6jdkcw40g := 0): 510 | assert c_6lxxujv6q, 0j 511 | gyzks8ae1sy = bha8g3_q7y9 512 | ''.xqnai0onv3l: 0.0 = agvyrf6z4we 513 | global po8uoqdpk7h 514 | import nsw09zjry5h, rx0w_4k1lj8 as ppnq0zpuk4w, eer2_awzvxs, tvskrpouyiy as r5nsn7l3vjr 515 | z13v6nua8g_ %= '' 516 | j9zfoe0gb93 517 | raise 0j 518 | from cayliek309v import aj88o8hol1s, xjb71zv5fqg, rvfeu_2_82m as l2uzv6m9wgy, wsookixdihh as rcactxle2_3, f5rp768g0q4, hoievan1rvn, he5z5ps3iqv as ed02d0ibere, i8yx3qiq8gg as cmnlwyrse5e, lgn4im7x65a 519 | bdgnj1q6foh = nxvj2gvsudm 520 | async with (yield jf2t2ykjl32) as sxt0grc1rok: 521 | raise 0 522 | d72g739amja.a96bl_c6wcq <<= False 523 | pass 524 | global zg7yptztz5y 525 | dghae4ztn5b[None]: cj1p4htdhcy = b'' 526 | None 527 | from kkv6rxik4wq import pyq0_q9th5g as bzgqj66aoyn, jh963tkphph, gmvp78fierq as a51virub46x 528 | import o4qwvm7xnqd, as5klbea4uz as wzwm3bcywad, k01t2b879o1 as whiozynq7y_ 529 | del b8sn2os54yz 530 | assert 0 531 | xxwb0z6tq9i = {not m1a1kz6515s} 532 | {False for yc3oer5o936 in vurmq85cyhe if bdgnj1q6foh if '' if b'' for kud_xk90olp in jgv773b368g if xpjutbj8ky9 if 0j}(h506z90o968, f'{0.0}{ymz7rw8oqhn}jxgvicggyfu{b''}o1wsiqbge_5fbqjpok7zxw{kud_xk90olp}{None}udockmn3pgfg3j4o18g67y', {l5yi4_su1e5: e3r1e7txrjq, l2uzv6m9wgy: 0, 0j: jntzp0qf0so, 0.0: '', m0z88tho37_: us6jdkcw40g, False: kwm1wbdmfos}, l1c1dwf0lt7=None, zk22f4ild08=hy_pfrrplcz - b'', b8sn2os54yz=(0, a51virub46x, al9c66wdt14, '', zk22f4ild08, 0.0, bnc33fegodk, 0j)) -------------------------------------------------------------------------------- /src/spew/generate.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import random as _random 3 | import sys 4 | from contextlib import contextmanager 5 | import typing 6 | from spew.names import generate as make_name 7 | import logging 8 | import enum 9 | from spew.randomcycle import rcycle 10 | 11 | logging.basicConfig(level=logging.INFO) 12 | logger = logging.getLogger(__name__) 13 | MAX_DEPTH = 3 14 | DEFAULT_WIDTH = 20 15 | 16 | 17 | class GeneratorConstraints(enum.Flag): 18 | ANY = enum.auto() 19 | ONLY_IN_LOOPS = enum.auto() 20 | ONLY_IN_FUNCTIONS = enum.auto() 21 | 22 | 23 | OPERATORS = [ 24 | ast.Add, 25 | ast.BitAnd, 26 | ast.BitOr, 27 | ast.BitXor, 28 | ast.Div, 29 | ast.FloorDiv, 30 | ast.LShift, 31 | ast.Mod, 32 | ast.Mult, 33 | ast.MatMult, 34 | ast.Pow, 35 | ast.RShift, 36 | ast.Sub, 37 | ] 38 | CMPOPS = [ 39 | ast.Eq, 40 | ast.NotEq, 41 | ast.Lt, 42 | ast.LtE, 43 | ast.Gt, 44 | ast.GtE, 45 | ast.Is, 46 | ast.IsNot, 47 | ast.In, 48 | ast.NotIn, 49 | ] 50 | 51 | 52 | class Context: 53 | depth: int 54 | in_loop: bool 55 | names: list[str] 56 | max_depth: int = MAX_DEPTH 57 | width: int = DEFAULT_WIDTH 58 | 59 | def __init__(self): 60 | self.depth = 0 61 | self.width = DEFAULT_WIDTH 62 | self.in_loop = False 63 | self.in_function = False 64 | self.names = [] 65 | 66 | @contextmanager 67 | def nested(self): 68 | if self.depth + 1 > self.max_depth: 69 | logger.debug("Max depth exceeded", stack_info=True) 70 | self.depth += 1 71 | yield 72 | self.depth -= 1 73 | 74 | @contextmanager 75 | def inloop(self): 76 | _prev_state = self.in_loop 77 | self.in_loop = True 78 | yield 79 | self.in_loop = _prev_state 80 | 81 | @contextmanager 82 | def infunction(self): 83 | _prev_state = self.in_function 84 | self.in_function = True 85 | yield 86 | self.in_function = _prev_state 87 | 88 | 89 | def make_text(ctx: Context) -> str: 90 | return make_name(ctx, new=True) # TODO : Do better 91 | 92 | 93 | boolcycle = rcycle([True, False]) 94 | 95 | 96 | def randbool(ctx: Context) -> bool: 97 | return next(boolcycle) 98 | 99 | 100 | T = typing.TypeVar("T") 101 | 102 | 103 | def randchoice(ctx: Context, choices: typing.Sequence[T]) -> T: 104 | return _random.choice(choices) 105 | 106 | 107 | def randint(ctx: Context, a: int, b: int) -> int: 108 | # TODO: Use context for reproducible results 109 | return _random.randint(a, b) 110 | 111 | 112 | def generate_arg(ctx: Context, allow_annotations=False) -> ast.arg: 113 | arg = ast.arg() 114 | arg.arg = make_name(ctx, new=True) 115 | if randbool(ctx) and allow_annotations: 116 | arg.annotation = generate_name(ctx) 117 | return arg 118 | 119 | 120 | TFunc = typing.TypeVar("TFunc", ast.FunctionDef, ast.AsyncFunctionDef) 121 | 122 | 123 | def _generate_function(f: TFunc, ctx: Context) -> TFunc: 124 | f.args = ast.arguments() 125 | n_args = randint(ctx, 0, ctx.width) 126 | f.args.args = [generate_arg(ctx, True) for _ in range(n_args)] 127 | f.args.posonlyargs = [] 128 | f.args.kwonlyargs = [] 129 | if randbool(ctx): 130 | f.args.defaults = [ 131 | generate_constant(ctx, values_only=True) for _ in range(n_args) 132 | ] 133 | else: 134 | f.args.defaults = [] 135 | f.name = make_name(ctx, new=True) 136 | with ctx.infunction(): 137 | f.body = generate_nested_stmts(ctx) 138 | if randbool(ctx): 139 | f.decorator_list = [] 140 | else: 141 | f.decorator_list = [ 142 | generate_expr(ctx) for _ in range(randint(ctx, 1, 3)) 143 | ] # TODO: vary length 144 | f.lineno = 1 145 | return f 146 | 147 | 148 | def generate_function(ctx: Context) -> ast.FunctionDef: 149 | f = ast.FunctionDef() 150 | return _generate_function(f, ctx) 151 | 152 | 153 | def generate_asyncfunction(ctx: Context) -> ast.AsyncFunctionDef: 154 | f = ast.AsyncFunctionDef() 155 | return _generate_function(f, ctx) 156 | 157 | 158 | def generate_class(ctx: Context) -> ast.ClassDef: 159 | c = ast.ClassDef() 160 | c.name = make_name(ctx, new=True) 161 | if randbool(ctx): # 50/50 chance of no bases 162 | c.bases = [] 163 | else: 164 | c.bases = [generate_expr(ctx) for _ in range(randint(ctx, 0, 3))] 165 | c.keywords = [] 166 | c.body = generate_nested_stmts(ctx) 167 | if randbool(ctx): # 50/50 chance of no decorator list 168 | c.decorator_list = [] 169 | else: 170 | c.decorator_list = [ 171 | generate_expr(ctx) for _ in range(randint(ctx, 1, ctx.width)) 172 | ] 173 | c.lineno = 1 174 | return c 175 | 176 | 177 | def generate_ellipsis(ctx: Context) -> ast.Ellipsis: 178 | return ast.Ellipsis() 179 | 180 | 181 | def generate_pass(ctx: Context) -> ast.Pass: 182 | return ast.Pass() 183 | 184 | 185 | def generate_break(ctx: Context) -> ast.Break: 186 | return ast.Break() 187 | 188 | 189 | def generate_continue(ctx: Context) -> ast.Continue: 190 | return ast.Continue() 191 | 192 | 193 | gen_cycle = rcycle([ast.Load, ast.Store, ast.Del]) 194 | 195 | 196 | def generate_attribute(ctx: Context) -> ast.Attribute: 197 | a = ast.Attribute() 198 | a.value = generate_expr(ctx) 199 | a.attr = make_name(ctx) 200 | a.ctx = next(gen_cycle)() 201 | return a 202 | 203 | 204 | generate_subscript_ctx = rcycle([ast.Load, ast.Store, ast.Del]) 205 | 206 | 207 | def generate_subscript(ctx: Context) -> ast.Subscript: 208 | s = ast.Subscript() 209 | s.value = generate_expr(ctx) 210 | if randbool(ctx): 211 | s.slice = generate_constant(ctx) # TODO : Generate Tuple elts slice 212 | else: 213 | s.slice = generate_slice(ctx) 214 | s.ctx = next(generate_subscript_ctx)() 215 | return s 216 | 217 | 218 | def generate_assign(ctx: Context) -> ast.Assign: 219 | asgn = ast.Assign() 220 | asgn.lineno = 1 221 | if randbool(ctx): 222 | asgn.targets = [generate_name(ctx, new=True)] 223 | else: 224 | asgn.targets = [ 225 | generate_name(ctx, new=True) for _ in range(randint(ctx, 1, ctx.width)) 226 | ] 227 | asgn.value = generate_expr(ctx) 228 | return asgn 229 | 230 | 231 | operators_cycle = rcycle(OPERATORS) 232 | 233 | 234 | def generate_augassign(ctx: Context) -> ast.AugAssign: 235 | asgn = ast.AugAssign() 236 | asgn.lineno = 1 237 | if randbool(ctx): 238 | asgn.target = generate_name(ctx, new=True) 239 | else: 240 | if randbool(ctx): 241 | asgn.target = generate_attribute(ctx) 242 | else: 243 | asgn.target = generate_subscript(ctx) 244 | asgn.value = generate_expr(ctx) 245 | asgn.op = next(operators_cycle)() 246 | return asgn 247 | 248 | 249 | def generate_annassign(ctx: Context) -> ast.AnnAssign: 250 | asgn = ast.AnnAssign() 251 | asgn.lineno = 1 252 | if randbool(ctx): 253 | asgn.target = generate_name(ctx, new=True) 254 | else: 255 | if randbool(ctx): 256 | asgn.target = generate_attribute(ctx) 257 | else: 258 | asgn.target = generate_subscript(ctx) 259 | 260 | # simple is a boolean integer set to True for a Name node in target 261 | # that do not appear in between parenthesis and are hence pure names 262 | # and not expressions. 263 | if randbool(ctx): 264 | asgn.simple = 1 265 | asgn.value = generate_name(ctx) 266 | else: 267 | asgn.simple = 0 268 | # value is a single optional node 269 | asgn.value = generate_expr(ctx) 270 | asgn.annotation = generate_expr(ctx) 271 | return asgn 272 | 273 | 274 | def generate_import(ctx: Context) -> ast.Import: 275 | im = ast.Import() 276 | im.names = [] 277 | for _ in range(randint(ctx, 1, ctx.width)): 278 | alias = ast.alias() 279 | alias.name = make_name(ctx) 280 | if randbool(ctx): 281 | alias.asname = make_name(ctx, new=True) 282 | 283 | im.names.append(alias) 284 | return im 285 | 286 | 287 | def generate_importfrom(ctx: Context) -> ast.ImportFrom: 288 | im = ast.ImportFrom() 289 | im.module = make_name(ctx) 290 | im.names = [] 291 | for _ in range(randint(ctx, 1, ctx.width)): 292 | alias = ast.alias() 293 | alias.name = make_name(ctx) 294 | if randbool(ctx): 295 | alias.asname = make_name(ctx, new=True) 296 | 297 | im.names.append(alias) 298 | return im 299 | 300 | 301 | def generate_name(ctx: Context, new: bool = False) -> ast.Name: 302 | name = ast.Name() 303 | name.id = make_name(ctx, new=new) 304 | return name 305 | 306 | 307 | constant_values_cycle = rcycle( 308 | [None, str(), bytes(), bool(), int(), float(), complex()] 309 | ) 310 | constant_values_with_ellipsis_cycle = rcycle( 311 | [None, str(), bytes(), bool(), int(), float(), complex(), Ellipsis] 312 | ) 313 | 314 | 315 | def generate_constant(ctx: Context, values_only=False) -> ast.Constant: 316 | c = ast.Constant() 317 | c.value = next(constant_values_cycle) 318 | return c 319 | 320 | 321 | def generate_str_constant(ctx: Context) -> ast.Constant: 322 | c = ast.Constant() 323 | c.value = str(make_text(ctx)) 324 | return c 325 | 326 | 327 | def generate_return(ctx: Context) -> ast.Return: 328 | r = ast.Return() 329 | if randbool(ctx): 330 | r.value = generate_expr(ctx) 331 | return r 332 | 333 | 334 | def generate_delete(ctx: Context) -> ast.Delete: 335 | d = ast.Delete() 336 | # TODO : Is expr, but jst doing name 337 | d.targets = [generate_name(ctx)] # TODO: Vary targets 338 | return d 339 | 340 | 341 | def generate_raise(ctx: Context) -> ast.Raise: 342 | r = ast.Raise() 343 | r.exc = generate_expr(ctx) 344 | return r 345 | 346 | 347 | def generate_global(ctx: Context) -> ast.Global: 348 | g = ast.Global() 349 | g.names = [make_name(ctx)] # TODO: Vary length 350 | return g 351 | 352 | 353 | def generate_nonlocal(ctx: Context) -> ast.Nonlocal: 354 | n = ast.Nonlocal() 355 | n.names = [make_name(ctx)] # TODO: Vary length 356 | return n 357 | 358 | 359 | TFor = typing.TypeVar("TFor", ast.For, ast.AsyncFor) 360 | 361 | 362 | def _generate_for(ctx: Context, f: TFor) -> TFor: 363 | # TODO : Set tuple or collection as target 364 | f.target = generate_name(ctx, new=True) # Can be expr, but just doing name 365 | f.iter = generate_expr(ctx) 366 | f.lineno = 1 367 | with ctx.inloop(): 368 | f.body = generate_nested_stmts(ctx) 369 | if randbool(ctx): 370 | f.orelse = generate_nested_stmts(ctx) 371 | else: 372 | f.orelse = [] # TODO: Raise bug report about this? 373 | return f 374 | 375 | 376 | def generate_for(ctx: Context) -> ast.For: 377 | return _generate_for(ctx, ast.For()) 378 | 379 | 380 | def generate_asyncfor(ctx: Context) -> ast.AsyncFor: 381 | return _generate_for(ctx, ast.AsyncFor()) 382 | 383 | 384 | def generate_while(ctx: Context) -> ast.While: 385 | w = ast.While() 386 | w.test = generate_expr(ctx) 387 | w.lineno = 1 388 | with ctx.inloop(): 389 | w.body = generate_nested_stmts(ctx) 390 | if randbool(ctx): 391 | w.orelse = generate_nested_stmts(ctx) 392 | else: 393 | w.orelse = [] 394 | return w 395 | 396 | 397 | def generate_if(ctx: Context) -> ast.If: 398 | i = ast.If() 399 | i.test = generate_expr(ctx) 400 | i.lineno = 1 401 | i.body = generate_nested_stmts(ctx) 402 | if randbool(ctx): 403 | i.orelse = generate_nested_stmts(ctx) 404 | else: 405 | i.orelse = [] 406 | return i 407 | 408 | 409 | TWith = typing.TypeVar("TWith", ast.With, ast.AsyncWith) 410 | 411 | 412 | def _generate_with(ctx: Context, w: TWith) -> TWith: 413 | w.lineno = 1 414 | w.items = [] 415 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 416 | withitem = ast.withitem() 417 | withitem.context_expr = generate_expr(ctx) 418 | if randbool(ctx): 419 | withitem.optional_vars = generate_name( 420 | ctx, new=True 421 | ) # TODO : Can be expr, but just doing name 422 | w.items.append(withitem) 423 | w.body = generate_nested_stmts(ctx) 424 | return w 425 | 426 | 427 | def generate_with(ctx: Context) -> ast.With: 428 | return _generate_with(ctx, ast.With()) 429 | 430 | 431 | def generate_asyncwith(ctx: Context) -> ast.AsyncWith: 432 | return _generate_with(ctx, ast.AsyncWith()) 433 | 434 | 435 | def generate_assert(ctx: Context) -> ast.Assert: 436 | a = ast.Assert() 437 | a.test = generate_expr(ctx) 438 | if randbool(ctx): 439 | a.msg = generate_expr(ctx) 440 | return a 441 | 442 | 443 | def generate_expression(ctx: Context) -> ast.Expr: 444 | e = ast.Expr() 445 | e.value = generate_expr(ctx) 446 | return e 447 | 448 | 449 | if sys.version_info < (3, 11): 450 | TTry = typing.TypeVar("TTry") 451 | else: 452 | TTry = typing.TypeVar("TTry", ast.Try, ast.TryStar) 453 | 454 | 455 | def _generate_try(ctx: Context, t: TTry) -> TTry: 456 | t.lineno = 1 457 | t.body = generate_nested_stmts(ctx) 458 | t.handlers = [] 459 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 460 | handler = ast.ExceptHandler() 461 | handler.lineno = 1 462 | handler.type = generate_expr(ctx) 463 | if randbool(ctx): 464 | handler.name = make_name(ctx, new=True) 465 | handler.body = generate_nested_stmts(ctx) 466 | t.handlers.append(handler) 467 | if randbool(ctx): 468 | t.orelse = generate_nested_stmts(ctx) 469 | else: 470 | t.orelse = [] 471 | if randbool(ctx): 472 | t.finalbody = generate_nested_stmts(ctx) 473 | else: 474 | t.finalbody = [] 475 | return t 476 | 477 | 478 | def generate_try(ctx: Context) -> ast.Try: 479 | return _generate_try(ctx, ast.Try()) 480 | 481 | 482 | def generate_trystar(ctx: Context) -> ast.TryStar: 483 | return _generate_try(ctx, ast.TryStar()) 484 | 485 | 486 | def generate_literal_pattern(ctx: Context) -> ast.Constant: 487 | return generate_constant(ctx, values_only=True) 488 | 489 | 490 | def generate_capture_pattern(ctx: Context) -> ast.Name: 491 | name = generate_name(ctx, new=True) # Must not start with _ but doesnt anyway 492 | return name 493 | 494 | 495 | def generate_wildcard_pattern(ctx: Context) -> ast.Name: 496 | name = ast.Name() 497 | name.id = "_" 498 | return name 499 | 500 | 501 | def generate_value_pattern(ctx: Context) -> ast.Name: 502 | name = ast.Name() 503 | name1 = make_name(ctx, new=True) 504 | name2 = make_name(ctx, new=True) 505 | name.id = f"{name1}.{name2}" # TODO : Vary length and depth 506 | return name 507 | 508 | 509 | CLOSED_PATTERNS = [ 510 | generate_literal_pattern, 511 | generate_capture_pattern, 512 | generate_wildcard_pattern, 513 | generate_value_pattern, 514 | # TODO... 515 | # group_pattern 516 | # sequence_pattern 517 | # mapping_pattern 518 | # class_pattern 519 | ] 520 | 521 | closed_patterns_cycle = rcycle(CLOSED_PATTERNS) 522 | 523 | 524 | def generate_closed_pattern(ctx: Context) -> typing.Union[ast.pattern, ast.Constant]: 525 | return next(closed_patterns_cycle)(ctx) 526 | 527 | 528 | def generate_matchvalue(ctx: Context) -> ast.MatchValue: 529 | m = ast.MatchValue() 530 | m.value = generate_constant(ctx, values_only=True) 531 | return m 532 | 533 | 534 | singleton_cycle = rcycle([None, True, False]) 535 | 536 | 537 | def generate_matchsingleton(ctx: Context) -> ast.MatchSingleton: 538 | m = ast.MatchSingleton() 539 | m.value = next(singleton_cycle) 540 | return m 541 | 542 | 543 | def generate_matchstar(ctx: Context) -> ast.MatchStar: 544 | m = ast.MatchStar() 545 | if randbool(ctx): 546 | m.name = make_name(ctx) 547 | return m 548 | 549 | 550 | MATCH_CONST_GENERATORS = [ 551 | generate_matchvalue, 552 | generate_matchsingleton, 553 | ] 554 | 555 | match_const_cycle = rcycle(MATCH_CONST_GENERATORS) 556 | 557 | 558 | def generate_matchsequence(ctx: Context) -> ast.MatchSequence: 559 | m = ast.MatchSequence() 560 | m.patterns = [ 561 | next(match_const_cycle)(ctx) 562 | for _ in range(randint(ctx, 1, 3)) # TODO: Vary length 563 | ] 564 | if randbool(ctx): 565 | m.patterns.append(generate_matchstar(ctx)) 566 | return m 567 | 568 | 569 | mapping_generator_cycle = rcycle( 570 | [ 571 | generate_matchvalue, 572 | generate_matchsingleton, 573 | ] 574 | ) 575 | 576 | 577 | def generate_matchmapping(ctx: Context) -> ast.MatchMapping: 578 | m = ast.MatchMapping() 579 | m.keys = [] 580 | m.patterns = [] 581 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 582 | with ctx.nested(): 583 | m.keys.append( 584 | generate_constant(ctx, values_only=True) 585 | ) # TODO: Handle value_pattern tokens 586 | m.patterns.append(next(mapping_generator_cycle)(ctx)) 587 | if randbool(ctx): 588 | m.rest = make_name(ctx) 589 | return m 590 | 591 | 592 | def generate_matchclass(ctx: Context) -> ast.MatchClass: 593 | m = ast.MatchClass() 594 | m.cls = generate_name(ctx) # TODO: Can be expr in ASDL but not in reality 595 | m.patterns = [] 596 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 597 | with ctx.nested(): 598 | m.patterns.append(next(match_const_cycle)(ctx)) 599 | m.kwd_attrs = [] 600 | m.kwd_patterns = [] 601 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 602 | with ctx.nested(): 603 | m.kwd_attrs.append(make_name(ctx)) 604 | m.kwd_patterns.append(next(match_const_cycle)(ctx)) 605 | return m 606 | 607 | 608 | def generate_matchas(ctx: Context) -> ast.MatchAs: 609 | m = ast.MatchAs() 610 | if randbool(ctx): 611 | m.pattern = next(closed_patterns_cycle)(ctx) 612 | if randbool(ctx): 613 | m.name = make_name(ctx, new=True) 614 | return m 615 | 616 | 617 | def generate_matchor(ctx: Context) -> ast.MatchOr: 618 | m = ast.MatchOr() 619 | m.patterns = [] 620 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 621 | m.patterns.append(next(closed_patterns_cycle)(ctx)) 622 | return m 623 | 624 | 625 | MATCH_GENERATORS = [ 626 | generate_matchvalue, 627 | generate_matchsingleton, 628 | generate_matchsequence, 629 | generate_matchmapping, 630 | generate_matchclass, 631 | # generate_matchstar, # Causes lots of problems with syntax? 632 | generate_matchas, 633 | generate_matchor, 634 | ] 635 | 636 | match_cycle = rcycle(MATCH_GENERATORS) 637 | 638 | 639 | def generate_matchpattern(ctx: Context) -> ast.pattern: 640 | """ 641 | MatchValue(expr value) 642 | | MatchSingleton(constant value) 643 | | MatchSequence(pattern* patterns) 644 | | MatchMapping(expr* keys, pattern* patterns, identifier? rest) 645 | | MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns) 646 | 647 | | MatchStar(identifier? name) 648 | -- The optional "rest" MatchMapping parameter handles capturing extra mapping keys 649 | 650 | | MatchAs(pattern? pattern, identifier? name) 651 | | MatchOr(pattern* patterns) 652 | """ 653 | return next(match_cycle)(ctx) 654 | 655 | 656 | def generate_match(ctx: Context) -> ast.Match: 657 | m = ast.Match() 658 | m.subject = generate_expr(ctx) 659 | m.cases = [] 660 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 661 | case = ast.match_case() 662 | case.pattern = generate_matchpattern(ctx) 663 | if randbool(ctx): 664 | case.guard = generate_expr(ctx) 665 | case.body = generate_nested_stmts(ctx) 666 | m.cases.append(case) 667 | return m 668 | 669 | 670 | # Constraint, is nested, generator 671 | STMT_GENERATORS = ( 672 | (GeneratorConstraints.ANY, False, generate_assign), 673 | (GeneratorConstraints.ANY, False, generate_augassign), 674 | (GeneratorConstraints.ANY, False, generate_annassign), 675 | (GeneratorConstraints.ANY, True, generate_function), 676 | (GeneratorConstraints.ANY, True, generate_asyncfunction), 677 | (GeneratorConstraints.ANY, True, generate_class), 678 | (GeneratorConstraints.ONLY_IN_FUNCTIONS, False, generate_return), 679 | (GeneratorConstraints.ANY, False, generate_delete), 680 | (GeneratorConstraints.ANY, True, generate_for), 681 | (GeneratorConstraints.ANY, True, generate_asyncfor), 682 | (GeneratorConstraints.ANY, True, generate_while), 683 | (GeneratorConstraints.ANY, True, generate_if), 684 | (GeneratorConstraints.ANY, True, generate_with), 685 | (GeneratorConstraints.ANY, True, generate_asyncwith), 686 | (GeneratorConstraints.ANY, True, generate_match), 687 | (GeneratorConstraints.ANY, False, generate_raise), 688 | (GeneratorConstraints.ANY, True, generate_try), 689 | (GeneratorConstraints.ANY, True, generate_trystar), 690 | (GeneratorConstraints.ANY, False, generate_assert), 691 | (GeneratorConstraints.ANY, False, generate_import), 692 | (GeneratorConstraints.ANY, False, generate_importfrom), 693 | (GeneratorConstraints.ANY, False, generate_global), 694 | (GeneratorConstraints.ONLY_IN_FUNCTIONS, False, generate_nonlocal), 695 | (GeneratorConstraints.ANY, False, generate_expression), 696 | (GeneratorConstraints.ANY, False, generate_pass), 697 | (GeneratorConstraints.ONLY_IN_LOOPS, False, generate_break), 698 | (GeneratorConstraints.ONLY_IN_LOOPS, False, generate_continue), 699 | # (GeneratorConstraints.ANY, generate_ellipsis), # This causes chaos 700 | ) 701 | 702 | list_ctx_cycle = rcycle([ast.Load, ast.Store, ast.Del]) 703 | 704 | 705 | def generate_list(ctx: Context) -> ast.List: 706 | l = ast.List() 707 | l.elts = generate_exprs(ctx) 708 | if randbool(ctx): 709 | l.ctx = next(list_ctx_cycle)() 710 | return l 711 | 712 | 713 | def generate_tuple(ctx: Context) -> ast.Tuple: 714 | t = ast.Tuple() 715 | t.elts = generate_exprs(ctx) 716 | return t 717 | 718 | 719 | bool_op_cycle = rcycle([ast.And, ast.Or]) 720 | 721 | 722 | def generate_boolop(ctx: Context) -> ast.BoolOp: 723 | b = ast.BoolOp() 724 | b.values = [generate_expr(ctx)] # TODO Vary length 725 | b.op = next(bool_op_cycle)() 726 | return b 727 | 728 | 729 | binop_cycle = rcycle(OPERATORS) 730 | 731 | 732 | def generate_binop(ctx: Context) -> ast.BinOp: 733 | b = ast.BinOp() 734 | b.left = generate_expr(ctx) 735 | b.right = generate_expr(ctx) 736 | b.op = next(binop_cycle)() 737 | return b 738 | 739 | 740 | unary_cycle = rcycle([ast.Invert, ast.Not, ast.UAdd, ast.USub]) 741 | 742 | 743 | def generate_unaryop(ctx: Context) -> ast.UnaryOp: 744 | u = ast.UnaryOp() 745 | u.operand = generate_expr(ctx) 746 | u.op = next(unary_cycle)() 747 | return u 748 | 749 | 750 | def generate_lambda(ctx: Context) -> ast.Lambda: 751 | l = ast.Lambda() 752 | l.args = ast.arguments() 753 | l.args.args = [generate_arg(ctx) for _ in range(randint(ctx, 0, ctx.width))] 754 | l.args.posonlyargs = [] 755 | l.args.kwonlyargs = [] 756 | l.args.defaults = [] 757 | l.body = generate_expr(ctx) 758 | return l 759 | 760 | 761 | def generate_ifexp(ctx: Context) -> ast.IfExp: 762 | i = ast.IfExp() 763 | i.test = generate_expr(ctx) 764 | i.body = generate_expr(ctx) 765 | i.orelse = generate_expr(ctx) 766 | return i 767 | 768 | 769 | def generate_dict(ctx: Context) -> ast.Dict: 770 | d = ast.Dict() 771 | d.keys = generate_exprs(ctx) 772 | d.values = generate_exprs(ctx) 773 | return d 774 | 775 | 776 | def generate_set(ctx: Context) -> ast.Set: 777 | s = ast.Set() 778 | s.elts = generate_exprs(ctx) 779 | return s 780 | 781 | 782 | def generate_comprehension(ctx: Context) -> ast.comprehension: 783 | c = ast.comprehension() 784 | c.target = generate_name(ctx, new=True) 785 | c.iter = generate_expr(ctx) 786 | c.ifs = [] 787 | for _ in range(randint(ctx, 0, 3)): # TODO: Vary length 788 | c.ifs.append(generate_expr(ctx)) 789 | c.is_async = False # TODO: Vary? 790 | return c 791 | 792 | 793 | def generate_listcomp(ctx: Context) -> ast.ListComp: 794 | l = ast.ListComp() 795 | l.elt = generate_expr(ctx) 796 | l.generators = [] 797 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 798 | l.generators.append(generate_comprehension(ctx)) 799 | return l 800 | 801 | 802 | def generate_setcomp(ctx: Context) -> ast.SetComp: 803 | s = ast.SetComp() 804 | s.elt = generate_expr(ctx) 805 | s.generators = [] 806 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 807 | s.generators.append(generate_comprehension(ctx)) 808 | return s 809 | 810 | 811 | def generate_dictcomp(ctx: Context) -> ast.DictComp: 812 | d = ast.DictComp() 813 | d.key = generate_expr(ctx) 814 | d.value = generate_expr(ctx) 815 | d.generators = [] 816 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 817 | d.generators.append(generate_comprehension(ctx)) 818 | return d 819 | 820 | 821 | def generate_generatorexp(ctx: Context) -> ast.GeneratorExp: 822 | g = ast.GeneratorExp() 823 | g.elt = generate_expr(ctx) 824 | g.generators = [] 825 | for _ in range(randint(ctx, 1, 3)): # TODO: Vary length 826 | g.generators.append(generate_comprehension(ctx)) 827 | return g 828 | 829 | 830 | def generate_await(ctx: Context) -> ast.Await: 831 | a = ast.Await() 832 | a.value = generate_expr(ctx) 833 | return a 834 | 835 | 836 | def generate_yield(ctx: Context) -> ast.Yield: 837 | y = ast.Yield() 838 | y.value = generate_expr(ctx) 839 | return y 840 | 841 | 842 | def generate_yieldfrom(ctx: Context) -> ast.YieldFrom: 843 | y = ast.YieldFrom() 844 | y.value = generate_expr(ctx) 845 | return y 846 | 847 | 848 | cmpop_cycle = rcycle(CMPOPS) 849 | 850 | 851 | def generate_compare(ctx: Context) -> ast.Compare: 852 | c = ast.Compare() 853 | c.left = generate_expr(ctx) 854 | c.comparators = [ 855 | generate_expr(ctx) for _ in range(randint(ctx, 1, 3)) 856 | ] # TODO: Use width or varied length 857 | c.ops = [ 858 | next(cmpop_cycle)() for _ in range(randint(ctx, 1, 3)) 859 | ] # TODO: Vary length 860 | return c 861 | 862 | 863 | def generate_call(ctx: Context) -> ast.Call: 864 | c = ast.Call() 865 | c.func = generate_expr(ctx) 866 | c.args = [] 867 | for _ in range(randint(ctx, 0, ctx.width // 2)): 868 | c.args.append(generate_expr(ctx)) 869 | c.keywords = [] 870 | for _ in range(randint(ctx, 0, ctx.width // 2)): 871 | kw = ast.keyword() 872 | kw.arg = make_name(ctx, new=True) 873 | kw.value = generate_expr(ctx) 874 | c.keywords.append(kw) 875 | return c 876 | 877 | 878 | def generate_formattedvalue(ctx: Context) -> ast.FormattedValue: 879 | f = ast.FormattedValue() 880 | # Use generate_name when Python < 3.12 881 | if sys.version_info < (3, 12): 882 | f.value = generate_name(ctx, new=True) 883 | else: 884 | f.value = generate_expr(ctx) 885 | f.format_spec = None # TODO : Generate format specs 886 | f.conversion = -1 # TODO : Work out what this is? 887 | return f 888 | 889 | 890 | def generate_joinedstr(ctx: Context) -> ast.JoinedStr: 891 | j = ast.JoinedStr() 892 | j.values = [ 893 | randchoice(ctx, [generate_str_constant, generate_formattedvalue])(ctx) 894 | for _ in range(ctx.width) 895 | ] 896 | return j 897 | 898 | 899 | def generate_namedexpr(ctx: Context) -> ast.NamedExpr: 900 | n = ast.NamedExpr() 901 | n.target = generate_name(ctx, new=True) 902 | n.value = generate_expr(ctx) 903 | return n 904 | 905 | 906 | def generate_slice(ctx: Context) -> ast.Slice: 907 | s = ast.Slice() 908 | s.lower = generate_expr(ctx) 909 | s.upper = generate_expr(ctx) 910 | s.step = generate_expr(ctx) 911 | return s 912 | 913 | 914 | EXPR_GENERATORS = ( 915 | generate_boolop, 916 | generate_namedexpr, 917 | generate_binop, 918 | generate_unaryop, 919 | generate_lambda, 920 | generate_ifexp, 921 | generate_dict, 922 | generate_set, 923 | generate_listcomp, 924 | generate_setcomp, 925 | generate_dictcomp, 926 | generate_generatorexp, 927 | generate_await, 928 | generate_yield, 929 | generate_yieldfrom, 930 | generate_compare, 931 | generate_call, 932 | generate_formattedvalue, 933 | generate_joinedstr, 934 | generate_constant, 935 | generate_attribute, 936 | generate_subscript, 937 | # generate_starred, 938 | generate_name, 939 | generate_list, 940 | generate_tuple, 941 | ) 942 | 943 | """ Expressions that don't themselves contain expressions. """ 944 | FLAT_EXPR_GENERATORS = [ 945 | generate_name, 946 | generate_constant, 947 | ] 948 | 949 | 950 | # Create another list with the first item in the tuple for each item in STMT_GENERATORS 951 | STMT_ALL_GENERATORS_ITER = rcycle(STMT_GENERATORS) 952 | 953 | 954 | def _generate_stmts(ctx: Context) -> list[ast.stmt]: 955 | if ctx.depth >= ctx.max_depth: 956 | logger.debug("Hit max depth for stmt") 957 | return [generate_pass(ctx)] 958 | stmts = [] 959 | while len(stmts) < ctx.width: 960 | next_stmt = next(STMT_ALL_GENERATORS_ITER) 961 | if next_stmt[1] and ctx.depth >= ctx.max_depth - 1: 962 | logger.debug("Hit max depth for nested stmt") 963 | continue 964 | if next_stmt[0] == GeneratorConstraints.ANY: 965 | stmts.append(next_stmt[2](ctx)) 966 | elif next_stmt[0] == GeneratorConstraints.ONLY_IN_FUNCTIONS and ctx.in_function: 967 | stmts.append(next_stmt[2](ctx)) 968 | elif next_stmt[0] == GeneratorConstraints.ONLY_IN_LOOPS and ctx.in_loop: 969 | stmts.append(next_stmt[2](ctx)) 970 | return stmts 971 | 972 | 973 | def generate_nested_stmts(ctx: Context) -> list[ast.stmt]: 974 | with ctx.nested(): 975 | return _generate_stmts(ctx) 976 | 977 | 978 | flat_expr_generators_cycle = rcycle(FLAT_EXPR_GENERATORS) 979 | expr_generators_cycle = rcycle(EXPR_GENERATORS) 980 | 981 | 982 | def generate_expr(ctx: Context) -> ast.expr: 983 | with ctx.nested(): 984 | if ctx.depth >= ctx.max_depth: 985 | logger.debug("Hit max depth") 986 | return next(flat_expr_generators_cycle)(ctx) 987 | return next(expr_generators_cycle)(ctx) 988 | 989 | 990 | def generate_exprs(ctx: Context) -> list[ast.expr]: 991 | if ctx.depth >= ctx.max_depth: 992 | return [] 993 | return [generate_expr(ctx) for _ in range(randint(ctx, 1, ctx.width))] 994 | 995 | 996 | def generate_module(depth: int, width: int, log_level: str | None = None) -> ast.Module: 997 | if log_level is not None: 998 | logger.setLevel(log_level) 999 | ctx = Context() 1000 | ctx.max_depth = depth 1001 | ctx.width = width 1002 | mod = ast.Module() 1003 | mod.type_ignores = [] 1004 | mod.body = generate_nested_stmts(ctx) 1005 | return mod 1006 | --------------------------------------------------------------------------------