From 5aac3804c1a8bb67112f5c66710cc793359e7ff6 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 04:52:33 +0200 Subject: [PATCH 1/8] ci: expose Qt test process crashes --- .github/workflows/tests.yml | 12 +++++++++++- run_tests.py | 10 +++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b0fd090..a5abb77 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,7 +66,17 @@ jobs: run: python -m compileall -q . - name: Run smoke tests + env: + PYTHONFAULTHANDLER: "1" run: python run_tests.py - name: Run full pytest suite - run: python -m pytest -ra -v + env: + PYTHONFAULTHANDLER: "1" + shell: bash + run: | + set +e + python -m pytest -ra -v + exit_code=$? + echo "Full pytest process return code: ${exit_code}" + exit "${exit_code}" diff --git a/run_tests.py b/run_tests.py index 1ea7f89..a872732 100644 --- a/run_tests.py +++ b/run_tests.py @@ -40,7 +40,7 @@ def main() -> int: env.setdefault("PYTHONIOENCODING", "utf-8") env.setdefault("QT_QPA_PLATFORM", "offscreen") - failed: list[str] = [] + failed: list[tuple[str, int]] = [] for test_file in TEST_FILES: print(f"\n=== {test_file} ===", flush=True) result = subprocess.run( @@ -51,10 +51,14 @@ def main() -> int: timeout=120, ) if result.returncode != 0: - failed.append(test_file) + failed.append((test_file, result.returncode)) if failed: - print("\nFAILED:", ", ".join(failed), flush=True) + failures = ", ".join( + f"{test_file} (return code {returncode})" + for test_file, returncode in failed + ) + print("\nFAILED:", failures, flush=True) return 1 print("\nAll smoke tests passed.", flush=True) From 5f29323ce2cde5a40c4448fe3bd1f910ebd448da Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 04:57:46 +0200 Subject: [PATCH 2/8] ci: compare isolated SFTP pytest process --- .github/workflows/tests.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5abb77..242ec7e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -80,3 +80,15 @@ jobs: exit_code=$? echo "Full pytest process return code: ${exit_code}" exit "${exit_code}" + + - name: Isolate SFTP tests under pytest (Python 3.10) + if: ${{ !cancelled() && matrix.python-version == '3.10' }} + env: + PYTHONFAULTHANDLER: "1" + shell: bash + run: | + set +e + python -m pytest -ra -v test_sftp_target.py + exit_code=$? + echo "SFTP-only pytest process return code: ${exit_code}" + exit "${exit_code}" From c2a09818fc4b9ed37021db1a26a219302ebbd5a1 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 05:05:51 +0200 Subject: [PATCH 3/8] ci: capture native SFTP abort backtrace --- .github/workflows/tests.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 242ec7e..ba517e2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -92,3 +92,36 @@ jobs: exit_code=$? echo "SFTP-only pytest process return code: ${exit_code}" exit "${exit_code}" + + - name: Capture native SFTP SIGABRT backtrace (Python 3.10) + if: ${{ !cancelled() && matrix.python-version == '3.10' }} + shell: bash + run: | + if ! command -v gdb >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y gdb + fi + gdb --version | head -n 1 + + commands_file="$(mktemp)" + trap 'rm -f "$commands_file"' EXIT + cat >"$commands_file" <<'GDB' + set pagination off + set debuginfod enabled off + set print frame-arguments none + handle SIGABRT nostop noprint pass + tcatch signal SIGABRT + commands + silent + printf "\n--- SIGABRT native backtrace (all threads) ---\n" + thread apply all bt + continue + end + run + GDB + + set +e + gdb --batch --quiet --nx --return-child-result -x "$commands_file" --args python test_sftp_target.py + exit_code=$? + echo "GDB-wrapped standalone SFTP process return code: ${exit_code}" + exit "${exit_code}" From 7e1829e613b266def2bf6ea4fd0fe59658cefa59 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 05:09:43 +0200 Subject: [PATCH 4/8] ci: trace Python allocator during SFTP smoke --- .github/workflows/tests.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ba517e2..7b0d821 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -125,3 +125,17 @@ jobs: exit_code=$? echo "GDB-wrapped standalone SFTP process return code: ${exit_code}" exit "${exit_code}" + + - name: Check standalone SFTP with Python allocator diagnostics (Python 3.10) + if: ${{ !cancelled() && matrix.python-version == '3.10' }} + env: + PYTHONFAULTHANDLER: "1" + PYTHONMALLOC: "debug" + PYTHONTRACEMALLOC: "5" + shell: bash + run: | + set +e + python test_sftp_target.py + exit_code=$? + echo "Debug-allocator standalone SFTP process shell exit status: ${exit_code}" + exit "${exit_code}" From 81353506f28cf14f56d9c84ada56152572914ffc Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 05:21:59 +0200 Subject: [PATCH 5/8] Cache dynamically loaded SFTP test module --- test_sftp_target.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test_sftp_target.py b/test_sftp_target.py index a6fc7e7..72edac9 100644 --- a/test_sftp_target.py +++ b/test_sftp_target.py @@ -12,11 +12,26 @@ from PySide6.QtCore import QCoreApplication +_PROSYNC_MODULE_NAME = "prosync_sftp" + + def load_prosync_module(): module_path = Path(__file__).with_name("ProSyncStart_V3.1.py") - spec = importlib.util.spec_from_file_location("prosync_sftp", module_path) + cached_module = sys.modules.get(_PROSYNC_MODULE_NAME) + if cached_module is not None: + return cached_module + + spec = importlib.util.spec_from_file_location(_PROSYNC_MODULE_NAME, module_path) + if spec is None or spec.loader is None: + raise ImportError(f"Could not load ProSync module from {module_path}") module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) + sys.modules[_PROSYNC_MODULE_NAME] = module + try: + spec.loader.exec_module(module) + except BaseException: + if sys.modules.get(_PROSYNC_MODULE_NAME) is module: + del sys.modules[_PROSYNC_MODULE_NAME] + raise return module From a45fdd4869c4671dc72ebb8dd6ce11800a1e204e Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 05:26:47 +0200 Subject: [PATCH 6/8] Revert "Cache dynamically loaded SFTP test module" This reverts commit 81353506f28cf14f56d9c84ada56152572914ffc. --- test_sftp_target.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/test_sftp_target.py b/test_sftp_target.py index 72edac9..a6fc7e7 100644 --- a/test_sftp_target.py +++ b/test_sftp_target.py @@ -12,26 +12,11 @@ from PySide6.QtCore import QCoreApplication -_PROSYNC_MODULE_NAME = "prosync_sftp" - - def load_prosync_module(): module_path = Path(__file__).with_name("ProSyncStart_V3.1.py") - cached_module = sys.modules.get(_PROSYNC_MODULE_NAME) - if cached_module is not None: - return cached_module - - spec = importlib.util.spec_from_file_location(_PROSYNC_MODULE_NAME, module_path) - if spec is None or spec.loader is None: - raise ImportError(f"Could not load ProSync module from {module_path}") + spec = importlib.util.spec_from_file_location("prosync_sftp", module_path) module = importlib.util.module_from_spec(spec) - sys.modules[_PROSYNC_MODULE_NAME] = module - try: - spec.loader.exec_module(module) - except BaseException: - if sys.modules.get(_PROSYNC_MODULE_NAME) is module: - del sys.modules[_PROSYNC_MODULE_NAME] - raise + spec.loader.exec_module(module) return module From c83e513972966e2298fc40e49478b9d0543c5398 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 05:36:51 +0200 Subject: [PATCH 7/8] ci: compare SFTP crash behavior across Qt versions --- .github/workflows/tests.yml | 141 ++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7b0d821..19f4cc5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -139,3 +139,144 @@ jobs: exit_code=$? echo "Debug-allocator standalone SFTP process shell exit status: ${exit_code}" exit "${exit_code}" + + - name: Compare SFTP behavior across PySide6 versions (Python 3.10) + if: ${{ !cancelled() && matrix.python-version == '3.10' }} + env: + PYTHONFAULTHANDLER: "1" + shell: bash + run: | + python - <<'PY' + import importlib.metadata + import os + import pathlib + import re + import subprocess + import sys + import tempfile + import venv + + project_root = pathlib.Path.cwd() + versions = ("6.11.2", "6.12.0") + results = [] + + def normalized_package(line): + return re.sub(r"[-_.]+", "-", line.split("==", 1)[0]).lower() + + def is_qt_package(name): + return name in {"pyside6", "shiboken6"} or name.startswith("pyside6-") + + with tempfile.TemporaryDirectory(prefix="prosync-qt-ab-", dir=os.environ["RUNNER_TEMP"]) as temp_dir: + root = pathlib.Path(temp_dir) + envs = {} + common_constraints = root / "common.constraints.txt" + common_lines = [] + install_args = ["-r", str(project_root / "requirements.txt"), "pytest", "Pillow", "tomli>=2.0.0; python_version < '3.11'"] + + for index, version in enumerate(versions): + env_dir = root / f"venv-{version}" + venv.EnvBuilder(with_pip=True).create(env_dir) + python = env_dir / "bin" / "python" + envs[version] = python + qt_constraints = root / f"qt-{version}.constraints.txt" + qt_constraints.write_text( + "\n".join(f"{name}=={version}" for name in ("PySide6", "PySide6-Addons", "PySide6-Essentials", "shiboken6")) + "\n", + encoding="utf-8", + ) + command = [str(python), "-m", "pip", "install"] + if index: + command.extend(("-c", str(common_constraints))) + command.extend(("-c", str(qt_constraints), *install_args)) + print(f"\nInstalling fresh Python {sys.version.split()[0]} venv for PySide6 {version}", flush=True) + install_status = subprocess.run(command, cwd=project_root, check=False).returncode + if install_status != 0: + print(f"A/B setup failed for PySide6 {version}: pip return code {install_status}", flush=True) + sys.exit(install_status) + + freeze = subprocess.run([str(python), "-m", "pip", "freeze"], capture_output=True, text=True, check=False) + if freeze.returncode != 0: + print(f"pip freeze failed for PySide6 {version}: {freeze.returncode}", flush=True) + sys.exit(freeze.returncode) + qt_family = [ + line for line in freeze.stdout.splitlines() + if "==" in line and is_qt_package(normalized_package(line)) + ] + non_qt = [ + line for line in freeze.stdout.splitlines() + if "==" in line and not is_qt_package(normalized_package(line)) + ] + print(f"PySide6 {version} Qt-family packages: {', '.join(sorted(qt_family))}", flush=True) + if index == 0: + common_lines = non_qt + common_constraints.write_text("\n".join(common_lines) + "\n", encoding="utf-8") + print(f"Pinned {len(common_lines)} non-Qt packages for the second environment", flush=True) + elif sorted(non_qt) != sorted(common_lines): + print("Non-Qt dependency versions differ between the fresh environments", flush=True) + results.append(("non-Qt dependency parity", 1)) + else: + print("Non-Qt dependency versions identical: True", flush=True) + + env = os.environ.copy() + env.update( + { + "PYTHONFAULTHANDLER": "1", + "PYTHONIOENCODING": "utf-8", + "QT_QPA_PLATFORM": "offscreen", + } + ) + + for version, python in envs.items(): + print(f"\n--- Environment versions: expected PySide6 {version} ---", flush=True) + version_code = ( + "import importlib.metadata, platform, sys; " + f"expected={version!r}; " + "print('python=' + platform.python_version()); " + "[print(name + '=' + importlib.metadata.version(dist)) " + "for name, dist in [('PySide6', 'PySide6'), ('shiboken6', 'shiboken6'), " + "('pytest', 'pytest'), ('paramiko', 'paramiko'), ('pypdf', 'pypdf'), " + "('tzdata', 'tzdata'), ('Pillow', 'Pillow')]]; " + "sys.exit(0 if (importlib.metadata.version('PySide6'), importlib.metadata.version('shiboken6')) == (expected, expected) else 1)" + ) + version_status = subprocess.run([str(python), "-c", version_code], cwd=project_root, check=False).returncode + if version_status != 0: + results.append((f"{version} package-version report", version_status)) + + for version, python in envs.items(): + commands = ( + ("standalone SFTP", [str(python), "test_sftp_target.py"]), + ( + "pytest SFTP", + [ + str(python), + "-m", + "pytest", + "-ra", + "-v", + "--basetemp", + str(root / f"pytest-temp-{version}"), + "test_sftp_target.py", + ], + ), + ) + for label, command in commands: + print(f"\n--- {label}: PySide6 {version} ---", flush=True) + process = subprocess.Popen(command, cwd=project_root, env=env) + timed_out = False + try: + status = process.wait(timeout=60) + except subprocess.TimeoutExpired: + timed_out = True + print(f"{label} exceeded 60 seconds; terminating its child process", flush=True) + process.kill() + status = process.wait() + print(f"{label} process return code (PySide6 {version}): {status}", flush=True) + results.append((f"{label} PySide6 {version}", status)) + if timed_out: + results.append((f"{label} timeout (PySide6 {version})", 1)) + + failures = [(label, status) for label, status in results if status != 0] + if failures: + print("\nA/B diagnostic failures:", failures, flush=True) + sys.exit(1) + print("\nBoth Qt-version environments and all four SFTP process runs completed successfully.", flush=True) + PY From 9df1fa944372fbd7a2a8e7117f9a8541e3ab179e Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 12 Oct 2026 06:08:49 +0200 Subject: [PATCH 8/8] fix: cap PySide6 below 6.12 temporarily --- .github/workflows/source-platform-smoke.yml | 2 +- .github/workflows/tests.yml | 200 -------------------- BEFUNDE.md | 19 ++ CHANGELOG.md | 12 ++ pyproject.toml | 2 +- requirements.txt | 2 +- 6 files changed, 34 insertions(+), 203 deletions(-) diff --git a/.github/workflows/source-platform-smoke.yml b/.github/workflows/source-platform-smoke.yml index fd55d26..cdc6186 100644 --- a/.github/workflows/source-platform-smoke.yml +++ b/.github/workflows/source-platform-smoke.yml @@ -63,7 +63,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install "PySide6>=6.5.0" paramiko tzdata pytest Pillow + python -m pip install "PySide6>=6.5.0,<6.12" paramiko tzdata pytest Pillow - name: Compile modules (bytecode validation) run: python -m compileall -q . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 19f4cc5..a5abb77 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -80,203 +80,3 @@ jobs: exit_code=$? echo "Full pytest process return code: ${exit_code}" exit "${exit_code}" - - - name: Isolate SFTP tests under pytest (Python 3.10) - if: ${{ !cancelled() && matrix.python-version == '3.10' }} - env: - PYTHONFAULTHANDLER: "1" - shell: bash - run: | - set +e - python -m pytest -ra -v test_sftp_target.py - exit_code=$? - echo "SFTP-only pytest process return code: ${exit_code}" - exit "${exit_code}" - - - name: Capture native SFTP SIGABRT backtrace (Python 3.10) - if: ${{ !cancelled() && matrix.python-version == '3.10' }} - shell: bash - run: | - if ! command -v gdb >/dev/null 2>&1; then - sudo apt-get update - sudo apt-get install -y gdb - fi - gdb --version | head -n 1 - - commands_file="$(mktemp)" - trap 'rm -f "$commands_file"' EXIT - cat >"$commands_file" <<'GDB' - set pagination off - set debuginfod enabled off - set print frame-arguments none - handle SIGABRT nostop noprint pass - tcatch signal SIGABRT - commands - silent - printf "\n--- SIGABRT native backtrace (all threads) ---\n" - thread apply all bt - continue - end - run - GDB - - set +e - gdb --batch --quiet --nx --return-child-result -x "$commands_file" --args python test_sftp_target.py - exit_code=$? - echo "GDB-wrapped standalone SFTP process return code: ${exit_code}" - exit "${exit_code}" - - - name: Check standalone SFTP with Python allocator diagnostics (Python 3.10) - if: ${{ !cancelled() && matrix.python-version == '3.10' }} - env: - PYTHONFAULTHANDLER: "1" - PYTHONMALLOC: "debug" - PYTHONTRACEMALLOC: "5" - shell: bash - run: | - set +e - python test_sftp_target.py - exit_code=$? - echo "Debug-allocator standalone SFTP process shell exit status: ${exit_code}" - exit "${exit_code}" - - - name: Compare SFTP behavior across PySide6 versions (Python 3.10) - if: ${{ !cancelled() && matrix.python-version == '3.10' }} - env: - PYTHONFAULTHANDLER: "1" - shell: bash - run: | - python - <<'PY' - import importlib.metadata - import os - import pathlib - import re - import subprocess - import sys - import tempfile - import venv - - project_root = pathlib.Path.cwd() - versions = ("6.11.2", "6.12.0") - results = [] - - def normalized_package(line): - return re.sub(r"[-_.]+", "-", line.split("==", 1)[0]).lower() - - def is_qt_package(name): - return name in {"pyside6", "shiboken6"} or name.startswith("pyside6-") - - with tempfile.TemporaryDirectory(prefix="prosync-qt-ab-", dir=os.environ["RUNNER_TEMP"]) as temp_dir: - root = pathlib.Path(temp_dir) - envs = {} - common_constraints = root / "common.constraints.txt" - common_lines = [] - install_args = ["-r", str(project_root / "requirements.txt"), "pytest", "Pillow", "tomli>=2.0.0; python_version < '3.11'"] - - for index, version in enumerate(versions): - env_dir = root / f"venv-{version}" - venv.EnvBuilder(with_pip=True).create(env_dir) - python = env_dir / "bin" / "python" - envs[version] = python - qt_constraints = root / f"qt-{version}.constraints.txt" - qt_constraints.write_text( - "\n".join(f"{name}=={version}" for name in ("PySide6", "PySide6-Addons", "PySide6-Essentials", "shiboken6")) + "\n", - encoding="utf-8", - ) - command = [str(python), "-m", "pip", "install"] - if index: - command.extend(("-c", str(common_constraints))) - command.extend(("-c", str(qt_constraints), *install_args)) - print(f"\nInstalling fresh Python {sys.version.split()[0]} venv for PySide6 {version}", flush=True) - install_status = subprocess.run(command, cwd=project_root, check=False).returncode - if install_status != 0: - print(f"A/B setup failed for PySide6 {version}: pip return code {install_status}", flush=True) - sys.exit(install_status) - - freeze = subprocess.run([str(python), "-m", "pip", "freeze"], capture_output=True, text=True, check=False) - if freeze.returncode != 0: - print(f"pip freeze failed for PySide6 {version}: {freeze.returncode}", flush=True) - sys.exit(freeze.returncode) - qt_family = [ - line for line in freeze.stdout.splitlines() - if "==" in line and is_qt_package(normalized_package(line)) - ] - non_qt = [ - line for line in freeze.stdout.splitlines() - if "==" in line and not is_qt_package(normalized_package(line)) - ] - print(f"PySide6 {version} Qt-family packages: {', '.join(sorted(qt_family))}", flush=True) - if index == 0: - common_lines = non_qt - common_constraints.write_text("\n".join(common_lines) + "\n", encoding="utf-8") - print(f"Pinned {len(common_lines)} non-Qt packages for the second environment", flush=True) - elif sorted(non_qt) != sorted(common_lines): - print("Non-Qt dependency versions differ between the fresh environments", flush=True) - results.append(("non-Qt dependency parity", 1)) - else: - print("Non-Qt dependency versions identical: True", flush=True) - - env = os.environ.copy() - env.update( - { - "PYTHONFAULTHANDLER": "1", - "PYTHONIOENCODING": "utf-8", - "QT_QPA_PLATFORM": "offscreen", - } - ) - - for version, python in envs.items(): - print(f"\n--- Environment versions: expected PySide6 {version} ---", flush=True) - version_code = ( - "import importlib.metadata, platform, sys; " - f"expected={version!r}; " - "print('python=' + platform.python_version()); " - "[print(name + '=' + importlib.metadata.version(dist)) " - "for name, dist in [('PySide6', 'PySide6'), ('shiboken6', 'shiboken6'), " - "('pytest', 'pytest'), ('paramiko', 'paramiko'), ('pypdf', 'pypdf'), " - "('tzdata', 'tzdata'), ('Pillow', 'Pillow')]]; " - "sys.exit(0 if (importlib.metadata.version('PySide6'), importlib.metadata.version('shiboken6')) == (expected, expected) else 1)" - ) - version_status = subprocess.run([str(python), "-c", version_code], cwd=project_root, check=False).returncode - if version_status != 0: - results.append((f"{version} package-version report", version_status)) - - for version, python in envs.items(): - commands = ( - ("standalone SFTP", [str(python), "test_sftp_target.py"]), - ( - "pytest SFTP", - [ - str(python), - "-m", - "pytest", - "-ra", - "-v", - "--basetemp", - str(root / f"pytest-temp-{version}"), - "test_sftp_target.py", - ], - ), - ) - for label, command in commands: - print(f"\n--- {label}: PySide6 {version} ---", flush=True) - process = subprocess.Popen(command, cwd=project_root, env=env) - timed_out = False - try: - status = process.wait(timeout=60) - except subprocess.TimeoutExpired: - timed_out = True - print(f"{label} exceeded 60 seconds; terminating its child process", flush=True) - process.kill() - status = process.wait() - print(f"{label} process return code (PySide6 {version}): {status}", flush=True) - results.append((f"{label} PySide6 {version}", status)) - if timed_out: - results.append((f"{label} timeout (PySide6 {version})", 1)) - - failures = [(label, status) for label, status in results if status != 0] - if failures: - print("\nA/B diagnostic failures:", failures, flush=True) - sys.exit(1) - print("\nBoth Qt-version environments and all four SFTP process runs completed successfully.", flush=True) - PY diff --git a/BEFUNDE.md b/BEFUNDE.md index 94f747e..f7c83e1 100644 --- a/BEFUNDE.md +++ b/BEFUNDE.md @@ -33,3 +33,22 @@ Es wurde keine Produktdatei verändert. Der dokumentarische Readback ist der einzige eigene Änderungsumfang; die Manifest-Fremdänderung bleibt erhalten. Vor dem Slice stand `master` auf `c0c5580` und war laut lokalem Readback vier Commits hinter `origin/master`; ein Push ist nicht Bestandteil der Rolle. + +### Befund 3: Standalone-SFTP-Abbruch mit PySide6 6.12.0 (2026-10-12) + +- **Fundort:** Linux/Python-3.10-A/B-Diagnose im [Smoke-Run 38200480460](https://github.com/file-bricks/ProSync/actions/runs/38200480460/job/114652552520). +- **Beleg:** Bei identischen Nicht-Qt-Abhängigkeiten endete der Standalone-SFTP-Test + mit PySide6/Shiboken6 6.11.2 mit Exit 0; die isolierte SFTP-Pytest-Suite bestand 9/9. + Mit 6.12.0 bestand Pytest weiter 9/9, doch der Standalone-Prozess gab seine + Erfolgsmeldung aus und brach anschließend mit Exit -6 und + `munmap_chunk(): invalid pointer` ab. +- **Einordnung:** Der A/B-Lauf verbindet den Abbruch mit dem gemessenen 6.12.0- + Distributionssatz auf diesem SFTP-Pfad. Die Ursache der Speicherbeschädigung bleibt + unbekannt. +- **Maßnahme:** Temporäre Grenze `PySide6>=6.5.0,<6.12` in `requirements.txt`, + `pyproject.toml` und den CI-Installationspfaden; Paketversion 3.2.0 bleibt + eingefroren. Die Diagnose-Sonderläufe werden aus dem finalen Workflow entfernt, + während alle regulären Smoke- und Volltestläufe erhalten bleiben. +- **Wiederöffnung:** Grenze erst neu bewerten, wenn ein neuerer Qt-Stand den + unveränderten Standalone-SFTP-Test, den vollständigen Smoke-Runner und die + vorhandenen vollständigen CI-Matrizen erfolgreich absolviert. diff --git a/CHANGELOG.md b/CHANGELOG.md index 147c0db..65e1f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/). ## [Unreleased] +### Behoben / Fixed (2026-10-12) +- **Temporäre PySide6-Kompatibilitätsgrenze:** `requirements.txt`, `pyproject.toml` und + beide CI-Installationswege begrenzen PySide6 auf `>=6.5.0,<6.12`. Ein kontrollierter + Linux/Python-3.10-A/B-Lauf mit identischen Nicht-Qt-Paketen bestand mit + PySide6/Shiboken6 6.11.2: Standalone-SFTP mit Exit 0 und pytest mit 9/9. Mit 6.12.0 + brach derselbe Standalone-Prozess nach seiner Erfolgsmeldung mit Exit -6 ab; pytest + bestand weiterhin mit 9/9. Das belegt die Kompatibilitätsgrenze für den gemessenen + SFTP-Pfad, aber nicht die Ursache der Speicherbeschädigung. Die Grenze wird erst neu + bewertet, wenn ein neuerer Qt-Stand den unveränderten Standalone-Smoke, den regulären + Smoke-Runner und die vollständigen vorhandenen CI-Matrizen besteht. Die Release-Version + 3.2.0 bleibt unverändert. + ### Hinzugefügt / Added (2026-10-08) - **Pfad B Repository-Marketing, Discoverability, Visuelle 4-Sichten-Topologie & Level 1 SBOM Re-Audit:** - **Strikte Version-Freeze-Disziplin (`T-20260920-167562623`):** Versionsnummer `3.2.0` in `pyproject.toml`, Quellcode und Manifesten strikt unverändert bewahrt. diff --git a/pyproject.toml b/pyproject.toml index 4288a94..8add1cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ classifiers = [ "Topic :: System :: Filesystems" ] dependencies = [ - "PySide6>=6.5.0", + "PySide6>=6.5.0,<6.12", "paramiko>=3.4.0", "pypdf>=4.0.0", "tzdata>=2025.2" diff --git a/requirements.txt b/requirements.txt index 91f345e..aeb5ec8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ # Supported Python: 3.10-3.12 (requires Python >= 3.10) # Core Dependencies -PySide6>=6.5.0 +PySide6>=6.5.0,<6.12 tzdata>=2025.2 # SFTP/SSH target support