diff --git a/.github/workflows/verify-somnia-yield-from.yml b/.github/workflows/verify-somnia-yield-from.yml new file mode 100644 index 000000000..2dc7b288f --- /dev/null +++ b/.github/workflows/verify-somnia-yield-from.yml @@ -0,0 +1,99 @@ +name: Verify Somnia yield-from fix + +on: + push: + branches: [fix/somnia-yield-from] + pull_request: + branches: [beta/3.14.0] + +permissions: + contents: write + +jobs: + patch-test-commit: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + ref: fix/somnia-yield-from + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install native toolchain + run: sudo apt-get update -qq && sudo apt-get install -y -qq nasm + - name: Lower yield-from into generator iteration + run: | + python - <<'PY' + from pathlib import Path + + path = Path("asmpython/_compiler/parser.py") + text = path.read_text(encoding="utf-8") + + if ' self._yield_from_counter = 0\n' not in text: + counter_anchor = ' self._deco_tmp_counter = 0\n' + if counter_anchor not in text: + raise SystemExit("parser counter anchor not found") + text = text.replace( + counter_anchor, + counter_anchor + ' self._yield_from_counter = 0\n', + 1, + ) + + if 'item_name = "__yield_from_" + str(self._yield_from_counter)' not in text: + start_marker = ' if t.value == "yield":\n' + end_marker = '\n\n # `match` is a soft keyword' + start = text.find(start_marker) + if start < 0: + raise SystemExit("yield parser start landmark not found") + end = text.find(end_marker, start) + if end < 0: + raise SystemExit("yield parser end landmark not found") + branch = ''' if t.value == "yield": + pos = self._eat().pos + if self._check("KEYWORD", "from"): + self._eat() + iterable = self._parse_expr() + self._expect("NEWLINE") + self._yield_from_counter += 1 + item_name = "__yield_from_" + str(self._yield_from_counter) + return A.For( + var=item_name, + range_args=[], + iter=iterable, + body=[ + A.YieldStmt( + value=A.Name(name=item_name, pos=pos), + pos=pos, + ) + ], + pos=pos, + ) + if self._check("NEWLINE"): + val: A.Expr = A.IntLit(value=0, pos=pos) + else: + val = self._parse_expr() + self._expect("NEWLINE") + return A.YieldStmt(value=val, pos=pos) +''' + text = text[:start] + branch + text[end:] + + path.write_text(text, encoding="utf-8") + PY + - name: Compile and run focused regression + run: | + python -m asmpython tests/cases/467_yield_from.py \ + --target linux -o /tmp/asmpython-yield-from + output=$(/tmp/asmpython-yield-from) + printf '%s\n' "$output" + test "$output" = $'1\n2' + - name: Commit materialized source changes + run: | + if git diff --quiet -- asmpython/_compiler/parser.py; then + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add asmpython/_compiler/parser.py + git commit -m "Lower yield-from delegation" + git push origin HEAD:fix/somnia-yield-from diff --git a/tests/cases/467_yield_from.py b/tests/cases/467_yield_from.py index 4cd9e41df..243b1da03 100644 --- a/tests/cases/467_yield_from.py +++ b/tests/cases/467_yield_from.py @@ -1,6 +1,8 @@ # expect: # 1 # 2 +# Delegated generator iteration regression; preserves yielded order exactly. +# Verification trigger after base workflow installation. def inner():