فهم البيئات الافتراضية في بايثون

أود أن أتحدث اليوم عن البيئات الافتراضية في بايثون (أو “venvs”)، ولكن أولاً، أحتاج إلى تغطية بعض الخلفية. لنفترض أنك تكتب برنامجًا، ويحتاج إلى الوصول إلى بعض التعليمات البرمجية الأخرى، على سبيل المثال في مكتبة كتبها شخص آخر، لتشغيله. كيف تجعل هذه التعليمات البرمجية الأخرى متاحة؟

تتمثل إحدى الطرق في إدراج نسخة من الكود الآخر في برنامجك الخاص. يُعرف هذا عمومًا باسم الربط الثابت، وعلى الرغم من أن تفاصيله قد تصبح معقدة، فإن المفهوم الأساسي بسيط، وقد كان موجودًا لفترة طويلة. لا تزال العديد من اللغات الحديثة الشائعة – بما في ذلك Go وRust – مرتبطة بشكل ثابت حصريًا أو شبه حصري.

ولكن الربط الثابت قد يكون مضيعة للوقت، فإذا كان لديك خمسة برامج تحتاج جميعها إلى نفس المكتبة، فإن الربط الثابت ينتهي عمومًا بتخزين خمس نسخ من تلك المكتبة. كما يتطلب الأمر إعادة تجميع النسخ الخمس في أي وقت تكون هناك حاجة فيه إلى ترقية المكتبة التي تعتمد عليها جميعها.

لذا فإن أحد البدائل هو الربط الديناميكي، حيث من المتوقع أن توجد المكتبات الضرورية في مكان ما ــ عادة على نفس الجهاز، ولكن ليس دائما ــ ويمكن تحميلها عند تشغيل البرنامج. وهذا يتيح لك مشاركة نسخة واحدة من المكتبة بين برامج متعددة (ومن هنا جاء المصطلح الشائع “المكتبة المشتركة”)، كما يبسط عملية الترقية: فما عليك سوى ترقية النسخة المشتركة الوحيدة، وسوف يرى كل برنامج الإصدار الجديد في المرة التالية التي يتم فيها تشغيله.

ولكن الربط الديناميكي له جانب سلبي خاص به: فعندما يحتاج برنامج قيد التشغيل إلى تحميل مكتبة، فإنه يحتاج إلى معرفة مكان العثور على تلك المكتبة. تاريخيًا، تم حل هذه المشكلة باختيار دليل عالمي على مستوى النظام لتثبيت المكتبات المشتركة فيه، أو ربما دليل عالمي يمكن لمسؤولي النظام التثبيت فيه، ثم دليل واحد لكل مستخدم (غالبًا في دليلهم الرئيسي) يمكن لغير المسؤولين العاديين تثبيت مكتباتهم فيه. ولكن مرة أخرى، هناك مشكلات محتملة: ماذا يحدث إذا كان لديك برامج مختلفة تريد مجموعات غير متوافقة من المكتبات المشتركة؟ هذا نوع من “جحيم التبعية“، كما يُعرف عادةً (ويتخذ أشكالًا أكثر تخصصًا، مثل “جحيم DLL” للطريقة التقليدية التي يتعامل بها Windows مع المكتبات المشتركة).

معلومات حول استيراد بايثون

نظرًا لأن بايثون كانت في الأصل لغة Unix-y في تسعينيات القرن العشرين، فمن غير المستغرب أن يكون لديها ارتباط ديناميكي (عبارة import) مع دليل حزمة/مكتبة مشترك. يمكنك إعداد هذا من خلال آليات مثل متغير البيئة PYTHONPATH، أو ضبط sys.path في وقت التشغيل وإدراج عدد أكبر أو أقل من الدلائل للبحث فيها، ولكن النتيجة لا تزال أن جميع برامج بايثون التي يتم تشغيلها بواسطة مستخدم معين سترى نفس مجموعة المكتبات والحزم المثبتة. مما قد يؤدي بالطبع إلى مواقف “جحيم التبعية”.

تعمل اللغات الأخرى على حل هذه المشكلة بطرق مختلفة؛ يمكنها إنتاج أرشيفات مجمعة لتطبيق/مشروع كامل وكل تبعياته (على سبيل المثال، Java)، أو استخدام تثبيتات إصدارات من المكتبات (شائعة مع مكتبات C المشتركة على أنظمة Unix-y المختلفة، على الرغم من أن الاتفاقيات الدقيقة تختلف من نظام إلى آخر)، أو توفير طرق أخرى للحصول على نسخ متعددة من مكتبة متاحة وحل النسخة الصحيحة في وقت التشغيل (يقوم دليل node_modules الشائع في تطبيقات JavaScript بذلك).

بايثون و لفترة طويلة، لم يكن لديه حل قياسي لهذه المشكلة. ومن المسلم به أن معظم ذلك كان أيضًا في فترة حيث كان توزيع وتثبيت حزم بايثون نفسها لا يزال فوضويًا بعض الشيء – لم تكن أدوات الحزم الأولى في بايثون تحتوي على أشياء مثل بيانات التبعيات أو القدرة على تنزيل وتثبيت الحزم من أرشيف مثل PyPI – لذلك ربما لم يكن هناك الكثير من الأشخاص المحتاجين إليها بعد.

(نعم، أنا متأكد من أن بعض الأشخاص الساخرين سيزعمون أنها لا تزال فوضوية، ولكن بصراحة pip تعمل بشكل جيد منذ سنوات، معظم المشاكل التي يراها الناس هذه الأيام تظل في تجميع ملحقات معقدة متعددة اللغات، والتي من المحتمل أن يكون لها دائمًا مستوى صعوبة أساسي أعلى)

مقدمة عن البيئة الافتراضية

تم التوصل إلى الحل في عام 2007، عندما تم نشر أول إصدار عام من حزمة virtualenv على فهرس حزم بايثون. كانت الفكرة وراء virtualenv هي عزل تطبيقات أو مشاريع بايثون المختلفة، التي تعمل على نفس النظام، عن بعضها البعض، وإعطاء كل منها مجموعة فريدة من الحزم المثبتة.

لقد ثبت أن هذه الفكرة كانت شائعة بما يكفي لدرجة أنه بعد ستة عشر عامًا، لا يزال مشروع virtualenv قويًا، وتم اعتماد مجموعة فرعية من وظائفه في مكتبة بايثون القياسية، كوحدة venv.

التنفيذ بسيط بشكل مدهش في الواقع. يتكون تثبيت بايثون من مفسّر بايثون ومجموعة من المكتبات لاستخدامها معه؛ موقع المكتبة الافتراضي خاص بالمنصة ولكن يمكن تجاوزه. تتكون البيئة الافتراضية، في جوهرها، من:

  • دليل يسمى bin/ يحتوي على مُفسِّر بايثون (عادةً، اختصار أو ارتباط رمزي إلى المُفسِّر “الأصلي” المستخدم لإنشاء البيئة) وأي ملفات قابلة للتنفيذ أو نقاط دخول أخرى لسطر الأوامر.
  • دليل باسم lib/ يحتوي على دليل فرعي باسم إصدار بايثون، ودليل فرعي باسم site-packages والذي سيحتوي على المكتبات المثبتة فعليًا. على سبيل المثال، في بايثون 3.11، سيكون هذا الدليل هو lib/python3.11/site-packages.
  • ملف الإعدادات، عندما يتم اكتشافه في موقع محدد بالنسبة للمترجم، فإنه يخبر بايثون بتجاوز البادئات الأساسية واستخدام أدلة البيئة الافتراضية.

الشيء الآخر الذي تحصل عليه بشكل قياسي في بيئة افتراضية هو مجموعة من نصوص “التنشيط” لمختلف واجهات سطر الأوامر؛ سيؤدي تشغيل النص المناسب لواجهة سطر الأوامر الخاصة بك إلى “تنشيط” البيئة الافتراضية، ولكن هذا يتألف فقط من تعيين متغير البيئة VIRTUAL_ENV كتلميح للأدوات، وتعديل متغير البيئة PATH (أو ما يعادله) للعثور على دليل bin/ للبيئة الافتراضية قبل أي دلائل أخرى، وضبط موجه الأوامر الخاص بك لتضمين اسم أو مسار البيئة الافتراضية. يؤدي “إلغاء تنشيط” البيئة الافتراضية (عادةً عن طريق كتابة deactivate) إلى التراجع عن تغييرات متغير البيئة هذه.

من السهل تجربة ذلك: يمكنك تشغيل الأمر python -m venv <path> لإنشاء بيئة افتراضية جديدة . ثم يمكنك فحصها لمعرفة محتوياتها، ومحاولة تنشيطها وتثبيت أشياء فيها، ثم إلغاء تنشيطها وحذفها.

ولكن قيل لي أن هذا الأمر معقد!

البيئات الافتراضية في حد ذاتها ليست معقدة بشكل خاص، ولكن يمكنك جعلها معقدة، إذا حاولت.

على سبيل المثال، يمكنك إنشاء الكثير منها ثم العبث يدويًا بمتغيرات البيئة المرتبطة بالمسار، مما قد يسبب ارتباكًا غير ممتع على الإطلاق حول ما سيتم تشغيله عند كتابة python (أو اسم البرنامج النصي القابل للتنفيذ في بايثون مثل pip)، أو المكان الذي ستستورد منه إذا قمت بتشغيل برنامج بايثون يحتوي على عبارات import.

إن تنشيط البيئات الافتراضية وإلغاء تنشيطها بشكل صريح، والانتباه إلى تلميح موجه الأوامر الذي يُظهِر البيئة النشطة، يساعد كثيرًا في منع هذا النوع من المشكلات. إذا أردت، يمكنك أيضًا الحصول على أدوات سير عمل للمطورين والتي ستقوم تلقائيًا بإنشاء البيئات الافتراضية وإدارتها وتنشيطها/إلغاء تنشيطها لك حسب الحاجة.

هناك عادة جيدة أخرى يمكنك اكتسابها وهي استدعاء أدوات بايثون، عندما يكون ذلك ممكنًا، عبر python -m . يأخذ هذا اسم الوحدة كحجة نهائية، ويشغل تلك الوحدة. تدعم العديد من أدوات المكتبات القياسية والجهات الخارجية الشائعة هذا (لذا، python -m venv كما هو موضح أعلاه، python -m pip لتشغيل pip، إلخ). إذا كانت البيئة الافتراضية نشطة ولم تقم بالعبث بمسار PATH الخاص بك بعد تنشيطه، فسيجد بايثون دائمًا مُفسِّر بايثون الخاص بتلك البيئة الافتراضية (وينطبق الشيء نفسه على أسماء المُفسِّرين المُنسَّقين؛ على سبيل المثال، في بيئة افتراضية بايثون 3.11، سيجد كل من python وpython3 وpython3.11 مُفسِّر البيئة الافتراضية).

أخيرًا، عند بناء حاوية Docker لتشغيل بعض أكواد بايثون (مثل تطبيق ويب تم إنشاؤه باستخدام Django أو إطار عمل آخر)، يجب عليك دائمًا إنشاء بيئة افتراضية، حتى إذا كنت لا تعتقد أنك ستحتاج إليها. وهذا يضمن أنه حتى إذا انتهى الأمر بمترجم بايثون آخر داخل الحاوية (على سبيل المثال، بسبب تثبيت حزمة نظام تعتمد على حزمة/مترجم بايثون للتوزيعة، وهو أمر سهل القيام به دون إدراك)، فلن تتلاعب بها أو بحزمها. والآن، تعمل العديد من توزيعات Linux على تبني آلية تتطلب منك استخدام بيئة افتراضية لتثبيت الحزم باستخدام pip (على سبيل المثال، سيعرض Debian 12 والإصدارات الأحدث خطأً يخبرك باستخدام apt، وليس pip، لتثبيت الحزم لمترجم بايثون “النظام” الافتراضي، وإنشاء بيئة افتراضية إذا كنت تريد استخدام pip).


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading