diff --git a/CHANGELOG.md b/CHANGELOG.md index b1eabcf..6d21b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,22 @@ QPyPack 项目的所有重要变更都将记录在此文件中。 # 更新日志 (Changelog) --- -## [2.8.0] - 2026-08-08 + +## [2.7.7] - 2026-08-09 + +#### 新增 +- **配置写入保护**:引入原子化保存机制,彻底杜绝因断电、死机或磁盘满导致的配置丢失/损坏问题。。 +- **提高编码兼容**:升级源码与依赖的读取逻辑,智能兼容 GBK、带 BOM 的 UTF-8 等多种复杂编码,告别乱码闪退。 +- **抗杀软拦截**:产物输出阶段加入智能重试机制,有效解决生成瞬间被安全软件锁定而导致的“权限不足”报错。 +- **稳定性增强**:增加全局异常日志捕获,并隔离了引擎编译时的环境变量,避免干扰主程序运行。 + +#### 修复 +- 修复在部分 RamDisk(内存盘)环境下打包时,因路径解析引发 [WinError 1] 导致程序崩溃的问题。 +- 修复设置面板中的“临时目录”选项未对虚拟环境和 PyInstaller 缓存完全生效的漏洞。 + +--- + +## [2.7.6] - 2026-08-08 #### 新增 - **项目沙箱智能感知**:基于脚本路径哈希与 Python 版本/架构生成唯一沙箱标识,加载项目脚本时自动感知识别已有环境并快速复用。 diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 08d8330..122486f 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -9,7 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- -## [2.8.0] - 2026-08-08 +## [2.7.7] - 2026-08-09 + +#### Added +- **Configuration Write Protection**: Introduced an atomic save mechanism to completely prevent configuration loss or corruption caused by power outages, system crashes, or full disks. +- **Enhanced Encoding Compatibility**: Upgraded the reading logic for source code and dependencies to intelligently support various complex encodings (such as GBK and UTF-8 with BOM), eliminating crashes and garbled text. +- **Antivirus Interception Resistance**: Added a smart retry mechanism to the build output stage, effectively resolving "Permission Denied" errors caused by security software temporarily locking files during generation. +- **Stability Enhancements**: Added global exception logging and isolated environment variables during engine compilation to avoid interfering with main program operations. + +#### Fixed +- Fixed a crash caused by path resolution issues resulting in `[WinError 1]` when packaging under certain RamDisk environments. +- Fixed a bug where the "Temporary Directory" option in the settings panel did not fully apply to virtual environments and the PyInstaller cache. + +--- + +## [2.7.6] - 2026-08-08 #### Added - **Smart Project Sandbox Sensing**: Generates unique sandbox identifiers based on script path hashes and Python version/architecture. Automatically detects and reuses existing virtual environments upon loading project scripts. diff --git a/pyproject.toml b/pyproject.toml index 2d3033d..cc5c8b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "qpypack" -version = "2.7.4" +version = "2.7.7" description = "A cross-platform GUI packaging and compilation suite based on PyInstaller and Nuitka." readme = "README_EN.md" authors = [ diff --git a/qpypack/locales/ar_SA.json b/qpypack/locales/ar_SA.json index 353bffa..4c32599 100644 --- a/qpypack/locales/ar_SA.json +++ b/qpypack/locales/ar_SA.json @@ -340,5 +340,6 @@ "[INFO] Creating virtual environment ({name})...": "[معلومة] إنشاء بيئة افتراضية ({name})...", "[INFO] Reusing existing virtual environment: {path}": "[معلومة] إعادة استخدام البيئة الافتراضية الموجودة: {path}", "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[تحذير] فشل في إزالة البيئة غير الصالحة، سيتم استخدام بيئة مؤقتة: {error}", - "[WARN] Python environment info detection fallback: {error}": "[تحذير] فشل في اكتشاف معلومات بيئة بايثون، سيتم استخدام طريقة بديلة: {error}" + "[WARN] Python environment info detection fallback: {error}": "[تحذير] فشل في اكتشاف معلومات بيئة بايثون، سيتم استخدام طريقة بديلة: {error}", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[معلومة] تم اكتشاف بيئة افتراضية موجودة تلقائيًا '{venv}'. تم تحديد خيار 'الاحتفاظ بالبيئة الافتراضية المحلية'." } diff --git a/qpypack/locales/de_DE.json b/qpypack/locales/de_DE.json index 7bb1015..c340a03 100644 --- a/qpypack/locales/de_DE.json +++ b/qpypack/locales/de_DE.json @@ -340,5 +340,6 @@ "[WARN] Target project is in a Cloud Sync directory (e.g. OneDrive/Dropbox). Cloud sync may temporarily lock build files.": "[WARN] Projekt befindet sich in einem Cloud-Ordner. Dies kann Dateien sperren.", "[WARN] Timestamp server connection timed out, performing local fast signing...": "[WARN] Zeitstempel-Server Zeitüberschreitung, führe lokale schnelle Signierung durch...", "[WARN] Usage of '__file__' detected. In PyInstaller One-File mode, use 'sys._MEIPASS' to reliably locate bundled resource files!": "[WARN] Nutzung von '__file__' erkannt. Nutzen Sie im Einzeldatei-Modus 'sys._MEIPASS', um Ressourcen zu lokalisieren!", - "\\nProgram execution completed, press Enter to exit...": "\\nAusführung beendet. Enter drücken..." + "\\nProgram execution completed, press Enter to exit...": "\\nAusführung beendet. Enter drücken...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Automatisch erkannte bestehende virtuelle Umgebung '{venv}'. 'Lokale Venv behalten' ist aktiviert." } diff --git a/qpypack/locales/es_ES.json b/qpypack/locales/es_ES.json index 52b13c2..16df618 100644 --- a/qpypack/locales/es_ES.json +++ b/qpypack/locales/es_ES.json @@ -340,5 +340,6 @@ "[INFO] Creating virtual environment ({name})...": "[INFO] Creando entorno virtual ({name})...", "[INFO] Reusing existing virtual environment: {path}": "[INFO] Reutilizando entorno virtual existente: {path}", "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[WARN] Fallo al purgar entorno no válido, usando sandbox temporal: {error}", - "[WARN] Python environment info detection fallback: {error}": "[WARN] Detección de información del entorno Python falló, usando respaldo: {error}" + "[WARN] Python environment info detection fallback: {error}": "[WARN] Detección de información del entorno Python falló, usando respaldo: {error}", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Entorno virtual existente '{venv}' auto-detectado. 'Mantener Venv Local' está marcado." } diff --git a/qpypack/locales/fr_FR.json b/qpypack/locales/fr_FR.json index d672537..177e5b0 100644 --- a/qpypack/locales/fr_FR.json +++ b/qpypack/locales/fr_FR.json @@ -340,5 +340,6 @@ "[WARN] Target project is in a Cloud Sync directory (e.g. OneDrive/Dropbox). Cloud sync may temporarily lock build files.": "[WARN] Le projet est dans un dossier Cloud. Les fichiers peuvent être verrouillés.", "[WARN] Timestamp server connection timed out, performing local fast signing...": "[WARN] Délai d'attente du serveur d'horodatage dépassé, signature rapide locale en cours...", "[WARN] Usage of '__file__' detected. In PyInstaller One-File mode, use 'sys._MEIPASS' to reliably locate bundled resource files!": "[WARN] Utilisation de '__file__' détectée. Dans le mode fichier unique, utilisez 'sys._MEIPASS' !", - "\\nProgram execution completed, press Enter to exit...": "\\nExécution terminée. Appuyez sur Entrée pour quitter..." + "\\nProgram execution completed, press Enter to exit...": "\\nExécution terminée. Appuyez sur Entrée pour quitter...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Environnement virtuel existant '{venv}' auto-détecté. 'Conserver le venv local' est coché." } diff --git a/qpypack/locales/it_IT.json b/qpypack/locales/it_IT.json index c0d7a4f..1451c54 100644 --- a/qpypack/locales/it_IT.json +++ b/qpypack/locales/it_IT.json @@ -313,5 +313,6 @@ "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[AVVISO] Impossibile eliminare l'ambiente non valido, ripiego su sandbox temporanea: {error}", "[WARN] Icon conversion to .icns failed, building without icon.": "[AVVISO] Conversione icona in .icns non riuscita, build senza icona.", "[WARN] Icon resource writing blocked (possibly locked by system/antivirus), triggering fallback protection...": "[AVVISO] Scrittura risorsa icona bloccata (possibilmente bloccata da sistema/antivirus), attivazione protezione di fallback...", - "[WARN] Low disk space detected on build target drive (Available: {free:.1f} GB, >= 5.0 GB recommended). Build process may interrupt due to disk exhaustion.": "[AVVISO] Spazio su disco insufficiente rilevato sull'unità di destinazione della build (Disponibile: {free:.1f} GB, >= 5.0 GB consigliati). Il processo di build potrebbe interrompersi per esaurimento dello spazio." + "[WARN] Low disk space detected on build target drive (Available: {free:.1f} GB, >= 5.0 GB recommended). Build process may interrupt due to disk exhaustion.": "[AVVISO] Spazio su disco insufficiente rilevato sull'unità di destinazione della build (Disponibile: {free:.1f} GB, >= 5.0 GB consigliati). Il processo di build potrebbe interrompersi per esaurimento dello spazio.", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Ambiente virtuale esistente '{venv}' rilevato automaticamente. 'Mantieni Venv Locale' è selezionato." } diff --git a/qpypack/locales/ja_JP.json b/qpypack/locales/ja_JP.json index 2b82f6b..1225e63 100644 --- a/qpypack/locales/ja_JP.json +++ b/qpypack/locales/ja_JP.json @@ -340,5 +340,6 @@ "[WARN] Target project is in a Cloud Sync directory (e.g. OneDrive/Dropbox). Cloud sync may temporarily lock build files.": "[WARN] クラウド同期フォルダに存在します。ファイルがロックされる可能性があります。", "[WARN] Timestamp server connection timed out, performing local fast signing...": "[WARN] タイムスタンプサーバー接続タイムアウト。ローカル高速署名を実行します...", "[WARN] Usage of '__file__' detected. In PyInstaller One-File mode, use 'sys._MEIPASS' to reliably locate bundled resource files!": "[WARN] '__file__' の使用を検出。単一ファイルモードでは 'sys._MEIPASS' を使用してください。", - "\\nProgram execution completed, press Enter to exit...": "\\n処理が完了しました。Enter キーを押して終了します..." + "\\nProgram execution completed, press Enter to exit...": "\\n処理が完了しました。Enter キーを押して終了します...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] 既存の仮想環境 '{venv}' を自動検出しました。'ローカルVenvを保持' がチェックされています。" } diff --git a/qpypack/locales/ko_KR.json b/qpypack/locales/ko_KR.json index 466f11e..d87cb06 100644 --- a/qpypack/locales/ko_KR.json +++ b/qpypack/locales/ko_KR.json @@ -340,5 +340,6 @@ "[WARN] Target project is in a Cloud Sync directory (e.g. OneDrive/Dropbox). Cloud sync may temporarily lock build files.": "[WARN] 프로젝트가 클라우드 동기화 폴더에 있습니다. 파일이 잠길 수 있습니다.", "[WARN] Timestamp server connection timed out, performing local fast signing...": "[WARN] 타임스탬프 서버 연결 시간 초과, 로컬 빠른 서명을 진행합니다...", "[WARN] Usage of '__file__' detected. In PyInstaller One-File mode, use 'sys._MEIPASS' to reliably locate bundled resource files!": "[WARN] '__file__' 사용이 감지되었습니다. 단일 파일 모드에서는 리소스 파일을 찾기 위해 'sys._MEIPASS'를 사용하세요!", - "\\nProgram execution completed, press Enter to exit...": "\\n실행 완료. 종료하려면 Enter를 누르세요..." + "\\nProgram execution completed, press Enter to exit...": "\\n실행 완료. 종료하려면 Enter를 누르세요...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[정보] 기존 가상 환경 '{venv}'을(를) 자동으로 감지했습니다. '로컬 Venv 유지'가 선택되어 있습니다." } diff --git a/qpypack/locales/nl_NL.json b/qpypack/locales/nl_NL.json index 9c42d53..5630f15 100644 --- a/qpypack/locales/nl_NL.json +++ b/qpypack/locales/nl_NL.json @@ -313,5 +313,6 @@ "[ERROR] Target file is running or occupied. Please close the existing application and try again.": "[FOUT] Doelbestand wordt uitgevoerd of is bezet. Sluit de bestaande toepassing en probeer opnieuw.", "[ERROR] The specified icon file does not exist: {path}": "[FOUT] Het opgegeven pictogrambestand bestaat niet: {path}", "[FAILED] Build interrupted exceptionally!\n\nCommon Troubleshooting:\n1. Environment Mismatch (Most Common): The selected Python version is incompatible with your source code. Please go to [Build Settings] to switch to the Python version you normally use for this code.\n2. Missing Dependencies: Click 'Detailed Mode' above to check for ModuleNotFoundError.\n3. Antivirus Block: Ensure your security software is not blocking the build process.\n\n(Note: This is usually caused by environment/code discrepancies rather than the packaging engine itself. Please check the detailed log for exact reasons.)": "[MISLUKT] Build onderbroken met uitzonderingen!\n\nVeelvoorkomende probleemoplossing:\n1. Omgevingsverschil (meest voorkomend): De geselecteerde Python-versie is incompatibel met uw broncode. Ga naar [Buildinstellingen] om over te schakelen naar de Python-versie die u normaal voor deze code gebruikt.\n2. Ontbrekende afhankelijkheden: Klik op 'Gedetailleerde modus' hierboven om te controleren op ModuleNotFoundError.\n3. Antivirusblokkering: Zorg ervoor dat uw beveiligingssoftware het buildproces niet blokkeert.\n\n(Let op: Dit wordt meestal veroorzaakt door omgevings-/codeverschillen en niet door de verpakkingsengine zelf. Raadpleeg het gedetailleerde logboek voor exacte redenen.)", - "[FAILED] Compilation interrupted with exceptions, please click 'Detailed Mode' above the log window for troubleshooting.": "[MISLUKT] Compilatie onderbroken met uitzonderingen. Klik op 'Gedetailleerde modus' boven het logvenster voor probleemoplossing." + "[FAILED] Compilation interrupted with exceptions, please click 'Detailed Mode' above the log window for troubleshooting.": "[MISLUKT] Compilatie onderbroken met uitzonderingen. Klik op 'Gedetailleerde modus' boven het logvenster voor probleemoplossing.", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Automatisch bestaande virtuele omgeving '{venv}' gedetecteerd. 'Lokale Venv Behouden' is aangevinkt." } diff --git a/qpypack/locales/pl_PL.json b/qpypack/locales/pl_PL.json index bf32141..487ff59 100644 --- a/qpypack/locales/pl_PL.json +++ b/qpypack/locales/pl_PL.json @@ -313,5 +313,6 @@ "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[OSTRZEŻENIE] Nie udało się wyczyścić nieprawidłowego środowiska, przełączanie na tymczasowy sandbox: {error}", "[WARN] Icon conversion to .icns failed, building without icon.": "[OSTRZEŻENIE] Konwersja ikony do .icns nie powiodła się, budowanie bez ikony.", "[WARN] Icon resource writing blocked (possibly locked by system/antivirus), triggering fallback protection...": "[OSTRZEŻENIE] Zablokowano zapis zasobu ikony (prawdopodobnie zablokowany przez system/antywirus), uruchamianie ochrony zastępczej...", - "[WARN] Low disk space detected on build target drive (Available: {free:.1f} GB, >= 5.0 GB recommended). Build process may interrupt due to disk exhaustion.": "[OSTRZEŻENIE] Wykryto mało miejsca na dysku docelowym (Dostępne: {free:.1f} GB, zalecane >= 5.0 GB). Proces budowania może zostać przerwany z powodu wyczerpania miejsca na dysku." + "[WARN] Low disk space detected on build target drive (Available: {free:.1f} GB, >= 5.0 GB recommended). Build process may interrupt due to disk exhaustion.": "[OSTRZEŻENIE] Wykryto mało miejsca na dysku docelowym (Dostępne: {free:.1f} GB, zalecane >= 5.0 GB). Proces budowania może zostać przerwany z powodu wyczerpania miejsca na dysku.", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Wykryto automatycznie istniejące środowisko wirtualne '{venv}'. Opcja 'Zachowaj lokalne środowisko' jest zaznaczona." } diff --git a/qpypack/locales/pt_BR.json b/qpypack/locales/pt_BR.json index 4d3247e..b7af54f 100644 --- a/qpypack/locales/pt_BR.json +++ b/qpypack/locales/pt_BR.json @@ -340,5 +340,6 @@ "[INFO] Creating virtual environment ({name})...": "[INFO] Criando ambiente virtual ({name})...", "[INFO] Reusing existing virtual environment: {path}": "[INFO] Reutilizando ambiente virtual existente: {path}", "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[WARN] Falha ao purgar ambiente inválido, usando sandbox temporário: {error}", - "[WARN] Python environment info detection fallback: {error}": "[WARN] Detecção de informações do ambiente Python com fallback: {error}" + "[WARN] Python environment info detection fallback: {error}": "[WARN] Detecção de informações do ambiente Python com fallback: {error}", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Ambiente virtual existente '{venv}' detectado automaticamente. 'Manter Venv Local' está marcado." } diff --git a/qpypack/locales/ru_RU.json b/qpypack/locales/ru_RU.json index 3c0410e..8ccded2 100644 --- a/qpypack/locales/ru_RU.json +++ b/qpypack/locales/ru_RU.json @@ -340,5 +340,6 @@ "[INFO] Creating virtual environment ({name})...": "[INFO] Создание виртуального окружения ({name})...", "[INFO] Reusing existing virtual environment: {path}": "[INFO] Повторное использование существующего виртуального окружения: {path}", "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[WARN] Не удалось очистить недействительное окружение, используется временная песочница: {error}", - "[WARN] Python environment info detection fallback: {error}": "[WARN] Запасной вариант определения информации об окружении Python: {error}" + "[WARN] Python environment info detection fallback: {error}": "[WARN] Запасной вариант определения информации об окружении Python: {error}", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[ИНФО] Автоматически обнаружено существующее виртуальное окружение '{venv}'. Опция 'Сохранить локальное виртуальное окружение' отмечена." } diff --git a/qpypack/locales/th_TH.json b/qpypack/locales/th_TH.json index 5a52259..8c67be6 100644 --- a/qpypack/locales/th_TH.json +++ b/qpypack/locales/th_TH.json @@ -340,5 +340,6 @@ "[WARN] Target project is in a Cloud Sync directory (e.g. OneDrive/Dropbox). Cloud sync may temporarily lock build files.": "[คำเตือน] โปรเจกต์เป้าหมายอยู่ในไดเรกทอรีซิงก์คลาวด์ (เช่น OneDrive/Dropbox) การซิงก์คลาวด์อาจล็อกไฟล์สร้างชั่วคราว", "[WARN] Timestamp server connection timed out, performing local fast signing...": "[คำเตือน] การเชื่อมต่อเซิร์ฟเวอร์ประทับเวลาหมดเวลา กำลังดำเนินการลงนามอย่างรวดเร็วในเครื่อง...", "[WARN] Usage of '__file__' detected. In PyInstaller One-File mode, use 'sys._MEIPASS' to reliably locate bundled resource files!": "[คำเตือน] ตรวจพบการใช้ '__file__' ในโหมดไฟล์เดียวของ PyInstaller ให้ใช้ 'sys._MEIPASS' เพื่อค้นหาไฟล์ทรัพยากรที่รวมไว้อย่างน่าเชื่อถือ!", - "\\nProgram execution completed, press Enter to exit...": "\\nการทำงานของโปรแกรมเสร็จสิ้น กด Enter เพื่อออก..." + "\\nProgram execution completed, press Enter to exit...": "\\nการทำงานของโปรแกรมเสร็จสิ้น กด Enter เพื่อออก...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] ตรวจพบสภาพแวดล้อมเสมือน '{venv}' ที่มีอยู่โดยอัตโนมัติ ตัวเลือก 'เก็บ Venv ในเครื่อง' ถูกเลือกไว้" } diff --git a/qpypack/locales/tr_TR.json b/qpypack/locales/tr_TR.json index d708708..cd0263e 100644 --- a/qpypack/locales/tr_TR.json +++ b/qpypack/locales/tr_TR.json @@ -313,5 +313,6 @@ "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[UYARI] Geçersiz ortam temizlenemedi, geçici kum havuzuna geri dönülüyor: {error}", "[WARN] Icon conversion to .icns failed, building without icon.": "[UYARI] Simgenin .icns'e dönüştürülmesi başarısız oldu, simge olmadan derleniyor.", "[WARN] Icon resource writing blocked (possibly locked by system/antivirus), triggering fallback protection...": "[UYARI] Simge kaynağı yazma engellendi (muhtemelen sistem/antivirüs tarafından kilitlendi), yedek koruma tetikleniyor...", - "[WARN] Low disk space detected on build target drive (Available: {free:.1f} GB, >= 5.0 GB recommended). Build process may interrupt due to disk exhaustion.": "[UYARI] Derleme hedef sürücüsünde düşük disk alanı algılandı (Kullanılabilir: {free:.1f} GB, önerilen >= 5.0 GB). Derleme işlemi disk tükenmesi nedeniyle kesintiye uğrayabilir." + "[WARN] Low disk space detected on build target drive (Available: {free:.1f} GB, >= 5.0 GB recommended). Build process may interrupt due to disk exhaustion.": "[UYARI] Derleme hedef sürücüsünde düşük disk alanı algılandı (Kullanılabilir: {free:.1f} GB, önerilen >= 5.0 GB). Derleme işlemi disk tükenmesi nedeniyle kesintiye uğrayabilir.", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[BİLGİ] Mevcut sanal ortam '{venv}' otomatik olarak algılandı. 'Yerel Venv'i Koru' işaretli." } diff --git a/qpypack/locales/vi_VN.json b/qpypack/locales/vi_VN.json index 5eaecc0..683fb00 100644 --- a/qpypack/locales/vi_VN.json +++ b/qpypack/locales/vi_VN.json @@ -313,5 +313,6 @@ "[WARN] Detected 'requests' or 'httpx'. Auto-bundling 'certifi' certificates to prevent SSL errors.": "[CẢNH BÁO] Phát hiện 'requests' hoặc 'httpx'. Tự động đóng gói chứng chỉ 'certifi' để ngăn lỗi SSL.", "[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}": "[CẢNH BÁO] Không thể dọn dẹp môi trường không hợp lệ, chuyển sang sandbox tạm thời: {error}", "[WARN] Icon conversion to .icns failed, building without icon.": "[CẢNH BÁO] Chuyển đổi biểu tượng sang .icns thất bại, xây dựng mà không có biểu tượng.", - "[WARN] Icon resource writing blocked (possibly locked by system/antivirus), triggering fallback protection...": "[CẢNH BÁO] Ghi tài nguyên biểu tượng bị chặn (có thể bị khóa bởi hệ thống/phần mềm diệt virus), kích hoạt bảo vệ dự phòng..." + "[WARN] Icon resource writing blocked (possibly locked by system/antivirus), triggering fallback protection...": "[CẢNH BÁO] Ghi tài nguyên biểu tượng bị chặn (có thể bị khóa bởi hệ thống/phần mềm diệt virus), kích hoạt bảo vệ dự phòng...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[THÔNG TIN] Tự động phát hiện môi trường ảo hiện có '{venv}'. Tùy chọn 'Giữ Venv Cục Bộ' đã được chọn." } diff --git a/qpypack/locales/zh_CN.json b/qpypack/locales/zh_CN.json index 2f1e57a..5fcf0eb 100644 --- a/qpypack/locales/zh_CN.json +++ b/qpypack/locales/zh_CN.json @@ -279,6 +279,7 @@ "[INFO] Analyzing source code and project dependencies...": "[INFO] 分析源码与依赖...", "[INFO] Applied digital signature with timestamp ({tsa})...": "[INFO] 已通过时间戳服务器 ({tsa}) 完成数字签名与盖章...", "[INFO] Applying smart digital signature...": "[INFO] 正在为应用程序添加数字签名...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.", "[INFO] Build Cancelled.": "[INFO] 构建已取消。", "[INFO] Build log exported to: {path}": "[INFO] 日志已导出: {path}", "[INFO] Compilation completed, archiving built files...": "[INFO] 编译完成,归档构建产物...", diff --git a/qpypack/locales/zh_TW.json b/qpypack/locales/zh_TW.json index f102415..8db46cd 100644 --- a/qpypack/locales/zh_TW.json +++ b/qpypack/locales/zh_TW.json @@ -340,5 +340,6 @@ "[WARN] Target project is in a Cloud Sync directory (e.g. OneDrive/Dropbox). Cloud sync may temporarily lock build files.": "[WARN] 專案位於雲端同步目錄,可能鎖定建置檔案。", "[WARN] Timestamp server connection timed out, performing local fast signing...": "[WARN] 時間戳記伺服器連線逾時,進行本地快速簽章...", "[WARN] Usage of '__file__' detected. In PyInstaller One-File mode, use 'sys._MEIPASS' to reliably locate bundled resource files!": "[WARN] 偵測到 '__file__' 的使用。在單一檔案模式下,請使用 'sys._MEIPASS' 定位資源路徑!", - "\\nProgram execution completed, press Enter to exit...": "\\n程式執行完畢,按下 Enter 鍵退出..." + "\\nProgram execution completed, press Enter to exit...": "\\n程式執行完畢,按下 Enter 鍵退出...", + "[INFO] Auto-detected existing virtual environment '{venv}'. 'Keep Local Venv' is checked.": "[資訊] 自動偵測到現有的虛擬環境 '{venv}'。已勾選「保留本機虛擬環境」。" } diff --git a/qpypack/main.py b/qpypack/main.py index 8425347..6fc2608 100644 --- a/qpypack/main.py +++ b/qpypack/main.py @@ -18,6 +18,14 @@ from concurrent.futures import ThreadPoolExecutor from pathlib import Path import logging +_orig_resolve = Path.resolve +def _safe_resolve(self, strict=False): + try: + return _orig_resolve(self, strict=strict) + except Exception: + return self.absolute() +Path.resolve = _safe_resolve + logger = logging.getLogger(__name__) if os.name == 'nt': @@ -52,7 +60,7 @@ except ImportError: HAS_QT_AUDIO = False __app_name__ = "QPyPack" -__version__ = "2.7.4" +__version__ = "2.7.7" __author__ = "QwejayHuang" __company__ = "QwejayHuang" __description__ = "Modern Cross-Platform Python Packaging GUI Powered by PyInstaller & Nuitka" @@ -751,9 +759,15 @@ def load_config(retry=True): def save_config(config): try: - with open(CONFIG_FILE, 'w', encoding='utf-8') as f: + temp_file = CONFIG_FILE + ".tmp" + with open(temp_file, 'w', encoding='utf-8') as f: config.write(f) - except: pass + if os.path.exists(CONFIG_FILE): + os.replace(temp_file, CONFIG_FILE) + else: + os.rename(temp_file, CONFIG_FILE) + except Exception: + pass def create_pleasant_audio_files(): success_wav = _CONFIG_DIR / "sound_success.wav" @@ -874,12 +888,7 @@ def extract_project_imports_via_ast(target_path, scan_dir: bool = False) -> set: for file_p in files_to_scan: try: - raw = file_p.read_bytes() - try: - code = raw.decode('utf-8-sig') - except Exception: - code = raw.decode(locale.getpreferredencoding(), errors='ignore') - + code = safe_read_text(file_p) tree = ast.parse(code, filename=file_p.as_posix()) for node in ast.walk(tree): if isinstance(node, ast.Import): @@ -890,7 +899,6 @@ def extract_project_imports_via_ast(target_path, scan_dir: bool = False) -> set: pass return imports - def query_target_env_packages(python_exe: str) -> dict: if not python_exe or not os.path.exists(python_exe): return {} @@ -1347,6 +1355,18 @@ def get_python_executable(): return find_system_python() +def safe_read_text(path: Path) -> str: + try: + raw = path.read_bytes() + for enc in ('utf-8-sig', 'utf-8', 'gbk', 'gb18030', 'big5', 'latin-1'): + try: + return raw.decode(enc) + except UnicodeDecodeError: + continue + return raw.decode(locale.getpreferredencoding(), errors='ignore') + except Exception: + return "" + def remove_readonly(func, path, exc_info=None): try: os.chmod(path, stat.S_IWRITE) @@ -3650,10 +3670,10 @@ class ScriptAnalysisThread(QThread): script_imports = set() try: - with open(self.path, 'r', encoding='utf-8', errors='ignore') as f: - content = f.read(10240) + content = safe_read_text(Path(self.path))[:10240] v_match = re.search(r'^(?:__version__|VERSION|version)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I) + if v_match: version = v_match.group(1) c_match = re.search(r'^(?:__company__|COMPANY)\s*=\s*[\'"]([^\'"]+)[\'"]', content, re.M | re.I) @@ -3714,6 +3734,9 @@ class PackingThread(QThread): clean_env = os.environ.copy() clean_env.pop("PYTHONHOME", None) + + for c_var in ("VIRTUAL_ENV", "CONDA_PREFIX", "CONDA_DEFAULT_ENV", "CONDA_PYTHON_EXE", "CONDA_PROMPT_MODIFIER"): + clean_env.pop(c_var, None) if not (extra_env and "PYTHONPATH" in extra_env): if not ("PYTHONPATH" in clean_env and "_qpypack_temp_" in clean_env["PYTHONPATH"]): @@ -3725,6 +3748,7 @@ class PackingThread(QThread): clean_env["LC_ALL"] = "en_US.UTF-8" clean_env["PYTHONUNBUFFERED"] = "1" clean_env["PLAYWRIGHT_BROWSERS_PATH"] = "0" + clean_env["NUITKA_ACCEPT_DOWNLOADS"] = "yes" if extra_env: clean_env.update(extra_env) @@ -3833,9 +3857,7 @@ class PackingThread(QThread): if not self.params['noconsole']: try: - raw = orig_path.read_bytes() - try: code = raw.decode('utf-8-sig') - except: code = raw.decode(locale.getpreferredencoding(), errors='ignore') + code = safe_read_text(orig_path) pause_prompt_str = _("\\nProgram execution completed, press Enter to exit...").replace('\\n', '\n') @@ -4099,7 +4121,6 @@ class PackingThread(QThread): silent_error=True, timeout=15) def run(self): - os.environ["NUITKA_ACCEPT_DOWNLOADS"] = "yes" engine = self.params['engine'] app_name = self.params.get('app_name', 'app').strip() or 'app' pip_idx = self.params.get('pip_index_url', '').strip() @@ -4111,6 +4132,15 @@ class PackingThread(QThread): script_dir = Path(self.params['script_path']).parent if self.params.get('script_path') else Path.cwd() + sandbox_mode = int(self.params.get('temp_sandbox_mode', 0) or 0) + if sandbox_mode == 0: + custom_temp_base = (script_dir / ".qpypack_build").resolve() + custom_temp_base.mkdir(parents=True, exist_ok=True) + temp_dir_arg = custom_temp_base.as_posix() + else: + custom_temp_base = Path(tempfile.gettempdir()).resolve() + temp_dir_arg = None + try: if self._is_cancelled: return self.build_finished.emit(False, _("[INFO] Build Cancelled."), []) @@ -4229,12 +4259,11 @@ class PackingThread(QThread): if req_file.exists(): try: if is_cloud_locked(req_file): raise ValueError("Requirements file is locked") - raw_req = req_file.read_bytes() - try: req_content = raw_req.decode('utf-8-sig') - except: req_content = raw_req.decode(locale.getpreferredencoding(), errors='ignore') + req_content = safe_read_text(req_file) for line in req_content.splitlines(): canon_name, full_spec = parse_req_line(line) + if canon_name and canon_name.lower() not in target_std_libs: final_dependencies[canon_name] = full_spec reqs_declared_pkgs.add(canon_name) @@ -4290,7 +4319,7 @@ class PackingThread(QThread): if self.params.get('keep_venv'): self.venv_dir = (script_dir / safe_venv_name).resolve() else: - self.venv_dir = Path(tempfile.mkdtemp(prefix=f"qpypack_env_{short_stem}_")).resolve() + self.venv_dir = Path(tempfile.mkdtemp(prefix=f"qpypack_env_{short_stem}_", dir=temp_dir_arg)).resolve() python_exe_in_venv = (self.venv_dir / ("Scripts/python.exe" if os.name == "nt" else "bin/python")).as_posix() @@ -4317,7 +4346,7 @@ class PackingThread(QThread): robust_rmtree(self.venv_dir) except Exception as e: self.progress.emit(_("[WARN] Failed to purge invalid environment, falling back to temp sandbox: {error}", error=str(e))) - self.venv_dir = Path(tempfile.mkdtemp(prefix=f"qpypack_env_{short_stem}_")).resolve() + self.venv_dir = Path(tempfile.mkdtemp(prefix=f"qpypack_env_{short_stem}_", dir=temp_dir_arg)).resolve() python_exe_in_venv = (self.venv_dir / ("Scripts/python.exe" if os.name == "nt" else "bin/python")).as_posix() self.progress.emit(_("[INFO] Creating virtual environment ({name})...", name=self.venv_dir.name)) @@ -4406,9 +4435,10 @@ class PackingThread(QThread): dedup_install_list = [pkg for pkg in list(final_install_dict.values()) if re.split(r'[=>