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:
@@ -610,11 +610,13 @@ function installPackages(pythonBin, requirementsPath, targetSitePackages) {
|
|||||||
// Install packages directly to target directory
|
// Install packages directly to target directory
|
||||||
// --no-compile: Don't create .pyc files (saves space, Python will work without them)
|
// --no-compile: Don't create .pyc files (saves space, Python will work without them)
|
||||||
// --target: Install to specific directory
|
// --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
|
// 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)
|
// like real_ladybug that must be compiled from source on Intel Mac (no PyPI wheel)
|
||||||
const pipArgs = [
|
const pipArgs = [
|
||||||
'-m', 'pip', 'install',
|
'-m', 'pip', 'install',
|
||||||
'--no-compile',
|
'--no-compile',
|
||||||
|
'--only-binary', 'pydantic,pydantic-core',
|
||||||
'--target', targetSitePackages,
|
'--target', targetSitePackages,
|
||||||
'-r', requirementsPath,
|
'-r', requirementsPath,
|
||||||
];
|
];
|
||||||
@@ -707,7 +709,7 @@ async function downloadPython(targetPlatform, targetArch, options = {}) {
|
|||||||
// Without this check, corrupted caches with missing packages would be accepted
|
// Without this check, corrupted caches with missing packages would be accepted
|
||||||
// Note: Same list exists in python-env-manager.ts - keep them in sync
|
// 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)
|
// 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 missingPackages = criticalPackages.filter(pkg => {
|
||||||
const pkgPath = path.join(sitePackagesDir, pkg);
|
const pkgPath = path.join(sitePackagesDir, pkg);
|
||||||
// Check both directory and __init__.py for more robust validation
|
// 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)
|
// Verify critical packages were installed before creating marker (fixes #416)
|
||||||
// Note: Same list exists in python-env-manager.ts - keep them in sync
|
// 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)
|
// 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 postInstallMissing = criticalPackages.filter(pkg => {
|
||||||
const pkgPath = path.join(sitePackagesDir, pkg);
|
const pkgPath = path.join(sitePackagesDir, pkg);
|
||||||
const initFile = path.join(pkgPath, '__init__.py');
|
const initFile = path.join(pkgPath, '__init__.py');
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export class PythonEnvManager extends EventEmitter {
|
|||||||
// This fixes GitHub issue #416 where marker exists but packages are missing
|
// This fixes GitHub issue #416 where marker exists but packages are missing
|
||||||
// Note: Same list exists in download-python.cjs - keep them in sync
|
// 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)
|
// 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)
|
// Check each package exists with valid structure (directory + __init__.py)
|
||||||
const missingPackages = criticalPackages.filter((pkg) => {
|
const missingPackages = criticalPackages.filter((pkg) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user