Repository navigation
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Caution Review failedThe pull request is closed. Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
CI failures: stale base, not the 3.15 change The branch is on
Rebasing on The next blocker on 3.15 is duckdb: 1.5.6 has no cp315 wheel yet, so the job will try a full source build. Keep 3.14 in the matrix and add 3.15 next to it until duckdb ships 3.15 wheels? |
|
Thank you. Yes, let's wait for duckdb having compatible binary wheel packages published. Thank you for creating a ticket about it already, @eavanvalkenburg. |
3d4f192 to
03f4c0d
Compare
b94439a to
0d650b2
Compare
This comment was marked as resolved.
This comment was marked as resolved.
|
CI run #39690369174 reveals that validation on Python 3.15 does not succeed yet. However, we haven't been able identify the root cause: The process gets cancelled with no obvious error message or other hints about it. @hampsterx or @coderabbitai: Can you find any clues why this goes south? I have re-ran the CI workflow three times already, so I'd consider the job to be "deterministically failing" -- but why? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -uo pipefail
echo '=== Run metadata ==='
gh api repos/dlt-contrib/dlt-filesystem/actions/runs/39690369174 --jq '{id,head_sha,path,event,status,conclusion,run_attempt,created_at,updated_at,html_url}'
echo '=== All jobs ==='
gh api --paginate 'repos/dlt-contrib/dlt-filesystem/actions/runs/39690369174/jobs?filter=all&per_page=100' --jq '.jobs[] | {id,name,status,conclusion,started_at,completed_at,steps,html_url}'
echo '=== Run log tail ==='
gh run view 39690369174 --repo dlt-contrib/dlt-filesystem --log 2>&1 | tail -220
echo '=== Workflow file discovery ==='
fd --hidden -t f -e yml -e yaml . .githubLength of output: 47462 🏁 Script executed: #!/bin/bash
set -uo pipefail
echo '=== Workflow outline ==='
ast-grep outline .github/workflows/tests.yml
echo '=== Workflow content ==='
cat -n .github/workflows/tests.yml
for job in 119534795387 119536561745 119542386305; do
echo "=== Job $job log tail ==="
gh api "repos/dlt-contrib/dlt-filesystem/actions/jobs/$job/logs" | tail -150
echo "=== Job $job check annotations ==="
check_url=$(gh api "repos/dlt-contrib/dlt-filesystem/actions/jobs/$job" --jq .check_run_url)
if [ -n "$check_url" ] && [ "$check_url" != null ]; then
gh api "$check_url" --jq '{conclusion,output}'
gh api "$check_url/annotations" --jq '.[] | {path,start_line,annotation_level,message}'
fi
done
echo '=== Test configuration discovery ==='
fd --hidden -t f '^(pyproject\.toml|Makefile|pytest\.ini|tox\.ini|conftest\.py)$'Length of output: 7452 🏁 Script executed: #!/bin/bash
set -uo pipefail
for job in 119534795387 119536561745 119542386305; do
echo "=== Job $job log tail ==="
gh api --allow-escape-sequences "repos/dlt-contrib/dlt-filesystem/actions/jobs/$job/logs" | tail -110
done
echo '=== Project configuration outline ==='
ast-grep outline pyproject.toml
echo '=== Project configuration ==='
cat -n pyproject.toml
echo '=== Test fixture outline ==='
ast-grep outline tests/conftest.pyLength of output: 57115 🏁 Script executed: #!/bin/bash
set -uo pipefail
echo '=== URI test outline ==='
ast-grep outline tests/test_util.py
echo '=== URI test code ==='
rg -n -C 30 'def test_requote_uri' tests/test_util.py
echo '=== URI implementation discovery ==='
rg -n -C 5 --type py 'def requote_uri|import .*requote_uri|from .* import.*requote_uri' .
echo '=== Latest job log selected evidence ==='
gh api --allow-escape-sequences repos/dlt-contrib/dlt-filesystem/actions/jobs/119542386305/logs | python -c '
import sys,re
lines=sys.stdin.read().splitlines()
pending={}
for line in lines:
if re.search(r"Python|requests==|urllib3==|coverage==|pytest.*==|Runner Image|Image:|Version:|Poe =>|worker|shutdown|Memory|memory|disk",line):
print(line)
result=re.search(r"\[gw\d+\].*?\b(PASSED|FAILED|SKIPPED|ERROR|XFAIL|XPASS)\s+(.*)",line)
if result:
pending.pop(result.group(2).strip(),None)
else:
test=re.search(r"\b(tests/\S+::.*)",line)
if test:
pending[test.group(1).strip()]=line
print("=== Test starts without matching result (may include formatting mismatches) ===")
for line in pending.values(): print(line)
'
echo '=== Python 3.10 equivalent test output ==='
gh api --allow-escape-sequences repos/dlt-contrib/dlt-filesystem/actions/jobs/119534795016/logs | grep -E 'sig=%80|Python 3|requests==|coverage=='Length of output: 11743 🏁 Script executed: #!/bin/bash
set -uo pipefail
echo '=== YAML test outline ==='
ast-grep outline tests/format/test_yaml.py
echo '=== Recursive alias test ==='
rg -n -C 25 'def test_recursive_alias_raises_cleanly' tests/format/test_yaml.py
echo '=== YAML codec discovery ==='
fd -t f -i 'yaml'
rg -n -C 5 --type py 'def .*yaml|safe_load|SafeLoader|RecursionError|recursive.alias' src tests/format/test_yaml.py
for job in 119534795387 119536561745 119542386305 119534795016; do
echo "=== Job $job YAML evidence ==="
gh api --allow-escape-sequences "repos/dlt-contrib/dlt-filesystem/actions/jobs/$job/logs" | grep -Ei 'pyyaml==|test_recursive_alias|test_yaml.py|shutdown signal' | tail -35
doneLength of output: 34167 🏁 Script executed: #!/bin/bash
set -uo pipefail
ast-grep outline src/dlt_filesystem/source/format/iterable_codec.py
sed -n '1,115p' src/dlt_filesystem/source/format/iterable_codec.py
sed -n '175,225p' src/dlt_filesystem/source/format/iterable_codec.py
rg -n -C 8 'normaliz|def read_via_iterable' src/dlt_filesystem/source/format/iterable_codec.py | tail -145Length of output: 17223
EvidenceIn each Python 3.15 attempt, worker
The same test passes in approximately one millisecond on Python 3.10. Worker See the latest failed job. Likely triggerAt Line 157 of a: &a [*a]The test expects normalization to terminate with This makes cyclic normalization the primary suspect, not DuckDB installation or the final URI test. Installation and standalone imports succeed. The logs do not prove why the runner shuts down. Memory exhaustion is a possible explanation, but there is no OOM diagnostic in the retrieved logs. A Python 3.15 or coverage interaction also remains unverified. Next diagnostic checkIn the Python 3.15 environment, run only this test without parallel workers. Use an external timeout to bound the run: timeout --kill-after=5s 60s \
python -m pytest -o addopts='' -vv \
-o faulthandler_timeout=15 \
tests/format/test_yaml.py::test_recursive_alias_raises_cleanlyThen repeat with coverage enabled: timeout --kill-after=5s 60s \
python -m pytest -o addopts='' -vv --cov \
-o faulthandler_timeout=15 \
tests/format/test_yaml.py::test_recursive_alias_raises_cleanlyThese checks should capture a stack trace before shutdown and distinguish a normalization problem from a coverage interaction. I could inspect the logs and code, but I could not execute this reproduction here. If the isolated test confirms the cycle traversal, the robust fix is explicit cycle detection before normalization. It should not depend on the interpreter eventually raising You are interacting with an AI system. |
ObservationIndeed when executed on Python 3.15, the process goes into an endless loop or infinite recursion, hogging one CPU core at 100%, impossible to be killed by a regular CTRL+C. The log output shows: .... with the main error location message RecapThe software test reads like this. def test_recursive_alias_raises_cleanly(tmp_path):
"""A self-recursive alias makes a cyclic structure that safe_load accepts but that cannot be
flattened into a finite record; normalization hits Python's recursion limit and raises a
bounded RecursionError rather than hanging or emitting corrupt data. Pathological input,
pinned here as an unsupported-but-safe boundary (not a code-execution or data-leak hole)."""
path = write_yaml(tmp_path / "cyclic.yaml", "a: &a [*a]\n")
with pytest.raises(RecursionError):
list(read_yaml(iter([FileItemStub(path)]), chunksize=10)) # ty: ignore[invalid-argument-type]Q&AIs that new behaviour intended? Any idea how to mitigate the problem? Should it be reported to the Python core team as a regression compared to Python 3.14? /cc @coderabbitai, @hampsterx |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -uo pipefail
ast-grep outline tests/format/test_yaml.py
rg -n -C 12 'def test_recursive_alias_raises_cleanly' tests/format/test_yaml.py
ast-grep outline src/dlt_filesystem/source/format/iterable_codec.py
rg -n -C 15 'map_nested_values_in_place|def _normalize_row|def read_yaml' src/dlt_filesystem/source/format/iterable_codec.py
fd -t f '^(pyproject\.toml|uv\.lock)$'
rg -n -C 4 'name = "dlt"|dlt[>=<~\[]|requires-python' pyproject.toml uv.lockLength of output: 11329 🤖 web_search executed:
Length of output: 16503 🏁 Script executed: #!/bin/bash
set -uo pipefail
for issue in 148260 131798; do
echo "=== CPython issue $issue ==="
gh api "repos/python/cpython/issues/$issue" --jq '{title,body,state,html_url}'
done
echo '=== dlt traversal implementation ==='
curl -fsSL https://raw.githubusercontent.com/dlt-hub/dlt/devel/dlt/common/utils.py | python -c '
import sys
s=sys.stdin.read().splitlines()
for i,line in enumerate(s):
if line.startswith("def map_nested_values_in_place("):
end=next((j for j in range(i+1,len(s)) if s[j].startswith("def ")), min(i+140,len(s)))
for j in range(max(0,i-2),end): print(f"{j+1}: {s[j]}")
'Length of output: 15406 🤖 web_search executed:
Length of output: 18707
What the traceback establishesThe hang occurs during recursive traversal in I inspected the upstream implementation. It recursively visits dictionaries, lists, and tuples without cycle detection. It does not catch The 100 repeated frames do not mean that recursion stopped at depth 100. Isolate the possible CPython regressionTry this standalone reproduction on both Python 3.14 and 3.15. It removes YAML, dlt, pytest, and coverage while preserving the relevant recursive call form: import faulthandler
import sys
def walk(value, *args, **kwargs):
for index, child in enumerate(value):
if isinstance(child, list):
value[index] = walk(child, *args, **kwargs)
return value
print(sys.version, flush=True)
print("recursion limit:", sys.getrecursionlimit(), flush=True)
cycle = []
cycle.append(cycle)
# Exit after dumping the stack, even if normal interruption fails.
# exit=True skips cleanup; use this only in this diagnostic process.
faulthandler.dump_traceback_later(10, exit=True)
try:
walk(cycle)
except RecursionError:
print("RecursionError raised as expected", flush=True)
finally:
faulthandler.cancel_dump_traceback_later()I have not executed this reproduction. If it hangs on 3.15 but raises
Disabling JIT is a diagnostic comparison, not a confirmed workaround. I did not find a verified matching CPython issue. Mitigation in this packageReject cycles explicitly before calling Use an iterative traversal with container identities on the active traversal path. Reject a container when its identity already occurs on that path. Do not reject every repeated identity: two fields can legitimately reference the same non-cyclic YAML anchor. For compatibility with the current test, the guard could raise an explicit This removes reliance on the interpreter eventually stopping cyclic traversal. Increasing the recursion limit would not fix the cycle. The test’s “unsupported-but-safe” description also needs qualification: this reproduction demonstrates an availability risk, although it does not demonstrate code execution or data disclosure. ✏️ Learnings added
You are interacting with an AI system. |
|
@hampsterx: Is your clanker able to verify this on Python 3.14 vs. Python 3.15 and derive a corresponding outcome? |
|
@amotl Confirmed locally on Linux x86_64 at
A dependency-free reproduction also confirms the difference: with the recursion limit set to 1,000, recursive calls forwarding This matches CPython #155151: the specialized I suggest retaining 3.14 in the matrix and rejecting cyclic input explicitly before enabling 3.15. |
|
Excellent. Thanks for discovering the corresponding upstream ticket.
Thank you. Feel free to submit a relevant patch, optimally stacked on this one. |
Use ty 0.0.91, matching main, when validating the stacked Python 3.15 patch.
Reject ancestor references in complete decoded values before splitting rows, so cyclic input cannot enter recursive normalization on Python 3.15. Retain finite aliases and cover cycles through CBOR tags and frozen mappings with timeout-bounded regressions. Keep Python 3.14 alongside 3.15 in CI.
Walk mapping keys as well as values because a CBOR tag can be its own frozen-mapping key. Pin that wire payload with a bounded reader regression.
|
Python 3.15 will be released today on 2026-10-01.Python 3.15 was released on 2026-10-09.