Pyodide: تشغيل بايثون الحقيقي في المتصفح عبر WebAssembly
يحوّل Pyodide مفسّر CPython إلى WebAssembly ليعمل Python مع NumPy وpandas مباشرةً في المتصفح. كيف يعمل وأين يكون مناسباً.

حلم تشغيل Python داخل المتصفح موجود منذ أوائل العقد الثاني من الألفية، وكل محاولة واجهت المشكلة نفسها: Python مرتبط بشكل عميق ببيئة تشغيل CPython المكتوبة بلغة C، ولذلك يصعب نقله إلى بيئة الويب. حاولت Transcrypt وBrython وSkulpt حل المشكلة بإعادة تنفيذ Python بلغة JavaScript، وهذا نجح مع السكربتات البسيطة، لكنه انهار عندما احتجت إلى NumPy أو pandas أو أي من مكتبات الإضافات المكتوبة بلغة C، وهي التي تجعل Python مفيداً فعلاً.
يتبع Pyodide نهجاً مختلفاً: تجميع مفسّر CPython الحقيقي إلى WebAssembly. ليست إعادة تنفيذ، بل شيفرة CPython المصدرية نفسها، مجمّعة بأداة Emscripten لتعمل في المتصفح. وهذا يعني توافقاً كاملاً مع اللغة، بما في ذلك إضافات C. يمكنك كتابة import numpy فيعمل، لأن الملف الثنائي Wasm المُجمّع يحتوي على شيفرة NumPy الفعلية بلغة C.
كيف يعمل Pyodide من الداخل
تأخذ عملية بناء Pyodide شجرة مصادر CPython وتجمّعها باستخدام Emscripten، وهي سلسلة أدوات تجميع تستهدف WebAssembly بدلاً من تعليمات المعالج الأصلية. والناتج ملف .wasm يطبّق مفسّر Python، إضافةً إلى شيفرة JavaScript الداعمة التي تربطه ببيئة المتصفح.
الجزء الصعب هو إضافات C. فمثلاً تحتوي NumPy على أكثر من 100,000 سطر من شيفرة C وFortran. يُجمّع Pyodide مسبقاً مجموعة منتقاة من الحزم الشائعة، مثل NumPy وpandas وscikit-learn وmatplotlib وscipy، في وحدات Wasm تُحمَّل عند الحاجة. وعند استدعاء import numpy، يحمّل Pyodide وحدة NumPy المُجمّعة مسبقاً، ويربطها بالمفسّر الجاري تشغيله، ثم يهيّئها.
<!-- Minimal Pyodide example -->
<script src="https://cdn.jsdelivr.net/pyodide/v0.27.0/full/pyodide.js"></script>
<script>
async function main() {
// Load the Python interpreter (~10MB download)
const pyodide = await loadPyodide();
// Run Python code directly
pyodide.runPython(`
import sys
print(f"Python {sys.version} running in the browser!")
`);
// Load packages on demand
await pyodide.loadPackage('numpy');
// Use NumPy — the real NumPy, compiled to Wasm
const result = pyodide.runPython(`
import numpy as np
arr = np.random.randn(1000)
f"Mean: {arr.mean():.4f}, Std: {arr.std():.4f}"
`);
console.log(result); // "Mean: 0.0123, Std: 1.0045"
}
main();
</script>
الجسر بين JavaScript وPython
من أقوى مزايا Pyodide التفاعل السلس بين Python وJavaScript. تُغلَّف كائنات Python تلقائياً كوكلاء (proxies) في JavaScript والعكس صحيح. يمكنك استدعاء دوال JavaScript من Python، والتعامل مع DOM من Python، وتمرير البيانات بين اللغتين دون تسلسل يدوي.
// JavaScript calling Python
const pyodide = await loadPyodide();
// Define a Python function
pyodide.runPython(`
def analyze(data):
import statistics
return {
'mean': statistics.mean(data),
'median': statistics.median(data),
'stdev': statistics.stdev(data)
}
`);
// Call it from JavaScript with JS data
const analyze = pyodide.globals.get('analyze');
const result = analyze([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
console.log(result.toJs()); // {mean: 5.5, median: 5.5, stdev: 3.03}
// Python accessing the DOM
pyodide.runPython(`
from js import document
element = document.getElementById('output')
element.textContent = 'Updated from Python!'
`);
يتولى نظام الوكلاء تحويل الأنواع تلقائياً: تتحول قواميس Python إلى كائنات JavaScript، وقوائمها إلى مصفوفات JavaScript، وأرقامها إلى أرقام JavaScript. وفي نقل البيانات الكبيرة، مثل تمرير مصفوفة NumPy من مليون عنصر إلى مكتبة تصور في JavaScript، يستخدم Pyodide مخازن ذاكرة مشتركة لتجنب النسخ، وهذا أمر حاسم للأداء.
متى يكون Pyodide منطقياً
بيئات الحوسبة التفاعلية. يعمل JupyterLite، المبني على Pyodide، على تشغيل دفاتر Jupyter بالكامل داخل المتصفح. لا حاجة إلى خادم، فنواة Python تعمل محلياً عبر Wasm. وهذا يغيّر قواعد اللعبة في التعليم: يستطيع الطلاب تشغيل دفاتر Python دون تثبيت أي شيء، ولا يحتاج المدرّس إلى توفير خوادم.
أدوات استكشاف البيانات. يمكن لتطبيقات الويب التي تسمح للمستخدمين برفع بياناتهم وتحليلها أن تستخدم Pyodide لمعالجة البيانات بالكامل من جهة العميل. لا تغادر البيانات المتصفح، ما يحل مخاوف الخصوصية ويلغي تكاليف الخوادم. رافع ملفات CSV يشغّل تحويلات pandas في المتصفح أسهل في النشر وأكثر خصوصية من آخر يرسل البيانات إلى الخادم الخلفي.
توثيق بأمثلة حية. يمكن لتوثيق مكتبات Python أن يتضمن كتل شيفرة تفاعلية تُنفَّذ فعلاً. بدلاً من عرض مخرجات ثابتة، يستطيع القارئ تعديل الشيفرة ورؤية النتائج فوراً. وهذا أكثر فاعلية بكثير للتعلم من الأمثلة الثابتة.
النماذج الأولية والتجريب. يستطيع علماء البيانات الذين يفكرون بلغة Python أن يبنوا نماذج أولية لتحويلات البيانات والتصورات مباشرة في المتصفح، ثم يشاركوا النتيجة كرابط. لا إعداد للبيئة، ولا إدارة للاعتمادية، ولا مشكلة «يعمل على جهازي فقط».
واقع الأداء
Pyodide ليس سريعاً. يعاني WebAssembly من تكلفة إضافية مقارنةً بالشيفرة الأصلية، وعادةً يكون أبطأ بمقدار 1.5 إلى 3 أضعاف في الأعمال الثقيلة حسابياً. وفوق ذلك، يعمل Pyodide على CPython نفسه، وهو بالفعل أبطأ بنحو 100 مرة من C في شيفرة Python الخالصة، ثم يُجمَّع إلى Wasm. في حلقات Python الخالصة، يكون Pyodide أبطأ من CPython الأصلي بنحو 2 إلى 5 أضعاف.
لكن الأمر المهم هنا: أحمال العمل التي يكون فيها Pyodide مفيداً تهيمن عليها استدعاءات إضافات C، لا شيفرة Python الخالصة. عندما تستدعي np.dot(a, b)، يحدث الحساب الفعلي في شيفرة C المُجمّعة (والمُجمّعة الآن إلى Wasm). تكلفة Wasm على شيفرة C هذه تبلغ 1.5 إلى 3 أضعاف، وهذا مقبول تماماً في الاستخدامات التفاعلية. ضرب مصفوفات في NumPy يستغرق 10 ملي ثانية أصلياً يستغرق 20 ملي ثانية في Pyodide، وهو فرق لا يُلاحظ لمستخدم يضغط زر «تشغيل» في دفتر ملاحظات.
Performance comparison (approximate):
Native CPython Pyodide (Wasm)
Pure Python loop: 1x 3-5x slower
NumPy operations: 1x 1.5-3x slower
Pandas groupby: 1x 2-3x slower
Startup time: 50ms 2-5 seconds
Package loading: instant (pip) 2-10s (download + init)
Startup is the real cost. Once loaded, interactive
performance is adequate for most use cases.
تكلفة البدء هي المشكلة الأكبر. تحميل نواة Pyodide الأساسية يتطلب تنزيل 10 ميغابايت. تحميل NumPy يضيف 7 ميغابايت أخرى، وpandas يضيف 10 ميغابايت. بالنسبة لصفحة ويب يجب أن تظهر بسرعة، يعد تأخير تهيئة مدته 5 ثوانٍ عائقاً حقيقياً. لهذا يعمل Pyodide بأفضل شكل في التطبيقات التي يتوقع فيها المستخدم انتظار تحميل بيئة العمل، مثل الدفاتر وأدوات البيانات والدروس التفاعلية، لا في صفحات الويب التقليدية.
ما الذي لا يستطيع Pyodide فعله
ليست كل حزم Python تعمل في Pyodide. الحزم التي لها اعتماديات على النظام، مثل مشغلات قواعد البيانات ومكتبات الواجهات الرسومية وأي شيء يستدعي واجهات خاصة بنظام التشغيل، لن تُجمَّع إلى Wasm. تحتاج psycopg2 إلى مكتبة عميل PostgreSQL، وتحتاج opencv إلى مكتبات رسوميات النظام. هذه الاعتماديات غير موجودة في بيئة المتصفح.
الشبكات محدودة أيضاً. وحدة socket في Python لا تعمل في المتصفح، إذ لا يوجد وصول مباشر إلى المقابس من Wasm. طلبات HTTP تمر عبر واجهة fetch() الخاصة بالمتصفح عبر جسر JavaScript في Pyodide، وبالتالي تخضع لقيود CORS. لا يمكنك تشغيل خادم Flask داخل Pyodide لأنه لا يوجد مقبس للاستماع عليه، ولا يمكنك إنشاء اتصالات شبكية عشوائية.
الخيوط مدعومة جزئياً عبر Web Workers، لكن قفل المفسّر العام GIL في Python لا يزال سارياً، ونموذج الخيوط يختلف عن CPython الأصلي. التوازي المقيّد بالمعالج يعمل بشكل أفضل مع multiprocessing (إذ يحصل كل عامل على نسخة Wasm خاصة به) منه مع الخيوط.
مقارنة Pyodide مع Python المُترجَم إلى JavaScript
يستحق الأمر مقارنة نهج Pyodide (تجميع CPython إلى Wasm) مع البديل (ترجمة Python إلى JavaScript). مشاريع مثل Transcrypt وBrython تحوّل صياغة Python إلى JavaScript، فينتج شيفرة تعمل مباشرة في محرك JavaScript الخاص بالمتصفح.
الترجمة أسرع وقت التشغيل، فالشيفرة المولّدة تعمل بسرعة JavaScript الأصلية، لا بسرعة CPython على Wasm. كما أن تكلفة البدء معدومة، إذ لا توجد بيئة تشغيل تحتاج إلى تنزيل. لكنها تضحي بالتوافق: لا يمكن لـ Python المترجَم تشغيل إضافات C، ويختلف دلالياً عن CPython بفروق دقيقة (خاصة في الأنواع الرقمية ومعالجة النصوص والحالات الحدّية)، ويدعم جزءاً فقط من المكتبة القياسية.
ميزة Pyodide أنه يشغّل CPython الحقيقي. إذا كانت شيفرة Python تعمل في CPython فستعمل في Pyodide (مع استثناء الاعتماديات على مستوى النظام). بالنسبة لأعمال علم البيانات التي تعتمد على NumPy وpandas وscikit-learn، هذا التوافق ضروري لا غنى عنه. أما السكربتات البسيطة التي لا تحتاج إلى إضافات C، فقد تكون الترجمة الخيار الأفضل، فكما هو الحال في أي مقارنة بين Wasm وJavaScript الأصلي، الأداة المناسبة تعتمد على طبيعة العمل.
إلى أين يتجه الأمر
Pyodide مشروع نشط ويتطور باستمرار. أدت الإصدارات الأخيرة إلى تقليص حجم نواة التشغيل، وتحسين وقت البدء عبر الترجمة المتدفقة (إذ يُجمّع المتصفح Wasm أثناء تنزيله)، وتوسيع مجموعة الحزم المُجمّعة مسبقاً. كما أن منظومة WebAssembly تنضج: توفر WASI واجهات نظام موحدة، وسيتيح نموذج المكونات (component model) تفاعلاً أفضل بين وحدات Wasm.
الاتجاه الأوسع أن المتصفح يصبح بيئة تشغيل شاملة. بين JavaScript وWasm ومشاريع مثل Pyodide، يمكنك تشغيل شيفرة مكتوبة بتقريباً أي لغة مباشرة في المتصفح. هذا لا يحل محل الحوسبة من جهة الخادم، بل يكملها بنقل الحسابات التي لا تحتاج خادماً إلى جهة العميل. ولمنظومة علم البيانات الضخمة في Python، يفتح التشغيل في المتصفح حالات استخدام لم تكن ممكنة من قبل.


