Fix pydantic_core missing module error during packaging (#806)

Fixes #684

## Problem
Users reported `ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'`
when running the packaged macOS app. This occurred because:

1. pydantic-core includes a compiled C extension (_pydantic_core.so)
2. During packaging, pip could attempt to build from source if no binary wheel found
3. Source builds could fail silently without a C compiler
4. The package would be marked as "installed" but missing the critical extension
5. pydantic_core was not in the critical packages verification list

## Solution
This fix implements two changes:

1. **Force binary wheels for pydantic packages**
   - Added `--only-binary pydantic,pydantic-core` to pip install args
   - Prevents silent source build failures
   - Ensures compiled extensions are properly included

2. **Add pydantic_core to critical packages verification**
   - Added to both download-python.cjs verification checks (lines 712, 815)
   - Added to python-env-manager.ts verification (line 129)
   - Ensures packaging fails fast if pydantic_core is missing

## Testing
The fix ensures that:
- Packaging will fail if pydantic binary wheels aren't available
- Both build-time and runtime verification check for pydantic_core
- Users won't receive a broken package with missing dependencies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: StillKnotKnown <192589389+StillKnotKnown@users.noreply.github.com>
Co-authored-by: Alex <63423455+AlexMadera@users.noreply.github.com>
Co-authored-by: Andy <119136210+AndyMik90@users.noreply.github.com>
This commit is contained in:
Maxim Kosterin
2026-01-08 15:04:13 +01:00
committed by GitHub
parent ada91fb195
commit 07ae1ef709
2 changed files with 5 additions and 3 deletions
+4 -2
View File
@@ -610,11 +610,13 @@ function installPackages(pythonBin, requirementsPath, targetSitePackages) {
// Install packages directly to target directory
// --no-compile: Don't create .pyc files (saves space, Python will work without them)
// --target: Install to specific directory
// --only-binary: Force binary wheels for pydantic (prevents silent source build failures)
// Note: We intentionally DO use pip's cache to preserve built wheels for packages
// like real_ladybug that must be compiled from source on Intel Mac (no PyPI wheel)
const pipArgs = [
'-m', 'pip', 'install',
'--no-compile',
'--only-binary', 'pydantic,pydantic-core',
'--target', targetSitePackages,
'-r', requirementsPath,
];
@@ -707,7 +709,7 @@ async function downloadPython(targetPlatform, targetArch, options = {}) {
// Without this check, corrupted caches with missing packages would be accepted
// Note: Same list exists in python-env-manager.ts - keep them in sync
// This validation assumes traditional Python packages with __init__.py (not PEP 420 namespace packages)
const criticalPackages = ['claude_agent_sdk', 'dotenv'];
const criticalPackages = ['claude_agent_sdk', 'dotenv', 'pydantic_core'];
const missingPackages = criticalPackages.filter(pkg => {
const pkgPath = path.join(sitePackagesDir, pkg);
// Check both directory and __init__.py for more robust validation
@@ -810,7 +812,7 @@ async function downloadPython(targetPlatform, targetArch, options = {}) {
// Verify critical packages were installed before creating marker (fixes #416)
// Note: Same list exists in python-env-manager.ts - keep them in sync
// This validation assumes traditional Python packages with __init__.py (not PEP 420 namespace packages)
const criticalPackages = ['claude_agent_sdk', 'dotenv'];
const criticalPackages = ['claude_agent_sdk', 'dotenv', 'pydantic_core'];
const postInstallMissing = criticalPackages.filter(pkg => {
const pkgPath = path.join(sitePackagesDir, pkg);
const initFile = path.join(pkgPath, '__init__.py');
+1 -1
View File
@@ -126,7 +126,7 @@ export class PythonEnvManager extends EventEmitter {
// This fixes GitHub issue #416 where marker exists but packages are missing
// Note: Same list exists in download-python.cjs - keep them in sync
// This validation assumes traditional Python packages with __init__.py (not PEP 420 namespace packages)
const criticalPackages = ['claude_agent_sdk', 'dotenv'];
const criticalPackages = ['claude_agent_sdk', 'dotenv', 'pydantic_core'];
// Check each package exists with valid structure (directory + __init__.py)
const missingPackages = criticalPackages.filter((pkg) => {