সরাসরি পর্বে যান
পর্ব ২৩ · ৫৫-এর মধ্যে · মডিউল ৪

pip, virtual environment আর pyproject — প্রতিটি প্রজেক্টের নিজের তাক

pip, Virtual Environments & pyproject.toml — Isolated, Reproducible, Safe Dependencies

Taught in Bangla, this lesson manages dependencies in Python: why each project needs its own virtual environment, what activate does to PATH, installing from PyPI with pip, version specifiers and requirements.txt, pyproject.toml, modern tools, the externally-managed-environment error, lock files and supply-chain safety.

এই পর্বে যা শিখবেন

  • PyPI, package, wheel আর sdist কী — আর pip install দিলে ভেতরে ধাপে ধাপে কী ঘটে
  • Windows, Mac, Linux-এ virtual environment তৈরি, activate আর deactivate করা; ভেতরে আছেন কি না নিশ্চিত হওয়া
  • Version-শর্ত, requirements.txt আর pyproject.toml দিয়ে প্রজেক্ট সহকর্মীর হাতে তুলে দেওয়া
  • uv, pipx, Poetry, conda — কোন টুল কেন আছে, আর externally-managed-environment error-এর মানে
  • Typosquatting আর AI-এর বানানো package-নামের বিপদ থেকে নিজেকে রক্ষা করা

১ · আপনার প্রজেক্ট অন্যের কোডের ওপর দাঁড়িয়ে

নাদিয়া দুটি প্রজেক্ট নিয়ে কাজ করেন। একটি তাঁর বিশ্ববিদ্যালয়ের দলগত প্রজেক্ট — লাইব্রেরির বই ধার দেওয়ার একটি ওয়েবসাইট, দুই বছর আগে Django 4.2 দিয়ে বানানো। অন্যটি নতুন একটি ফ্রিল্যান্সিং কাজ — একটি দোকানের অর্ডার নেওয়ার সাইট, ক্লায়েন্ট চেয়েছেন Django 5.2. নাদিয়া নতুন কাজের জন্য Django আপডেট করলেন। পরদিন পুরোনো প্রজেক্ট চালাতেই লাল লেখা:

ImportError: cannot import name 'utc' from 'django.utils.timezone'

নাদিয়া পুরোনো প্রজেক্টের একটি লাইনও বদলাননি। তবু সেটি ভেঙে গেল। কারণ তাঁর কম্পিউটারে Django একটিই, আর দুটি প্রজেক্ট সেই একই Django ভাগ করছিল। Django 5.0-এ পুরোনো timezone.utc নামটি সরিয়ে দেওয়া হয়েছে — নতুন version-এর জন্য ঠিক, পুরোনো কোডের জন্য বিপদ।

এই পর্বের মূল সমস্যা এটিই। আজকের প্রায় কোনো Python প্রজেক্ট শুধু নিজের কোডে চলে না। ওয়েবসাইটে Django বা FastAPI, ডেটায় pandas, ইন্টারনেট থেকে ডেটা আনতে requests — এগুলো অন্য মানুষের লেখা package. আপনার প্রজেক্ট যেসব বাইরের package ছাড়া চলে না, সেগুলোই তার dependency. আর প্রতিটি dependency-র নির্দিষ্ট একটি version আপনার কোডের সাথে মেলে।

একটি মেসবাড়ির কথা ভাবুন। সবাই একটি রান্নাঘরের এক তাক ভাগ করলে একজন নতুন ব্র্যান্ডের মসলা আনলে অন্যজনের চেনা রান্নার স্বাদ বদলে যায়। সমাধান — প্রত্যেকের নিজের তাক, যেখানে যার যে মসলা দরকার, ঠিক সেটিই থাকে। Python-এ সেই আলাদা তাকের নাম virtual environment, আর বাজার থেকে মসলা এনে তাকে সাজিয়ে রাখার লোকটির নাম pip. বাজারটি হলো PyPI.

বাজার, বাহক আর তাক

PyPI stores packages, pip fetches them, a venv keeps them per project

  1. PyPIpypi.orgলাখো package-এর ভাণ্ডার
  2. pippython -m pip installখুঁজে, নামিয়ে, বসিয়ে দেয়
  3. venv.venv/এক প্রজেক্টের নিজস্ব তাক
ছবি ২৩.১ — PyPI থেকে pip package এনে বর্তমান প্রজেক্টের তাকে রাখে।

Package, distribution, wheel, sdist — কয়েকটি শব্দ পরিষ্কার করি

পর্ব ২২-এ দেখেছেন, module হলো একটি .py ফাইল, আর import করা যায় এমন package হলো module-এর একটি ফোল্ডার। কিন্তু PyPI-তে যা আপলোড হয় আর pip যা ইনস্টল করে, তার আসল নাম distribution — নাম আর version-সহ একটি বান্ডিল, যার ভেতরে এক বা একাধিক import-যোগ্য package থাকে। দৈনন্দিন কথায় দুটিকেই “package” বলা হয়, আর তাতে সমস্যা নেই। শুধু জেনে রাখুন, দুটির নাম সবসময় এক নয়: pip install beautifulsoup4 করে কোডে লেখেন import bs4, আর pip install pillow করে লেখেন import PIL.

একটি distribution দুই রূপে আসতে পারে:

🛞 Wheel (.whl)

আগে থেকে build করা, তৈরি অবস্থার ফাইল। ইনস্টল মানে শুধু ঠিক জায়গায় ফাইল কপি — দ্রুত, compiler লাগে না। wheel-এর নামেই লেখা থাকে কোন Python আর কোন OS-এর জন্য।

requests-2.32.3-py3-none-any.whl

📦 sdist (.tar.gz)

সোর্স কোডের আর্কাইভ। ইনস্টলের সময় আপনার কম্পিউটারে build করতে হয়; ভেতরে C-কোড থাকলে compiler লাগে, সময় বেশি লাগে, ভুলও বেশি হয়। sdist ব্যবহার হয় তখনই, যখন আপনার OS-এর জন্য wheel নেই।

মিলের wheel না পেলে শেষ উপায়

wheel-এর নামের শেষ তিনটি অংশ পড়তে শিখুন। py3-none-any মানে “যেকোনো Python 3, কোনো C-অংশ নেই, যেকোনো OS” — বিশুদ্ধ Python package. আর NumPy-র মতো package-এর নামে থাকে cp313-cp313-win_amd64 — অর্থাৎ CPython 3.13, 64-বিট Windows-এর জন্য compile করা C extension. এজন্যই একদম নতুন Python version বেরোলে প্রথম কয়েক সপ্তাহ কিছু package ইনস্টল হতে চায় না — তাদের wheel তখনও তৈরি হয়নি।

C/C++ জানলে

C/C++-এ বহু বছর কোনো সর্বজনীন package-ভাণ্ডার ছিল না — library আনতে হতো OS-এর package manager (apt) দিয়ে, হাতে নামিয়ে, বা CMake-এর FetchContent, vcpkg, Conan দিয়ে। Python-এ শুরু থেকেই একটি কেন্দ্রীয় ভাণ্ডার (PyPI) আর একটি মানক ইনস্টলার (pip) আছে। তবে মূল সমস্যা একই: header আর library-র version না মিললে যেমন C++ build ভাঙে, Python-এও তেমনি। তুলনার জন্য C++ কোর্সের CMake ও build পর্বের “বাইরের library” অংশ দেখুন।

২ · Virtual environment — প্রতিটি প্রজেক্টের নিজের তাক

নাদিয়ার সমস্যার সমাধান হলো প্রতিটি প্রজেক্টের ফোল্ডারে একটি করে virtual environment. এটি আসলে একটি সাধারণ ফোল্ডার (প্রথা অনুযায়ী নাম .venv), যার ভেতরে থাকে আসল Python-এর দিকে নির্দেশ করা একটি ছোট python, আর package রাখার নিজস্ব একটি site-packages ফোল্ডার। এই ফোল্ডারে যা ইনস্টল হয়, তা অন্য কোনো প্রজেক্ট দেখতে পায় না।

নিচের ডায়াগ্রামে দেখুন, একই কম্পিউটারে একই package-এর দুটি version কীভাবে শান্তিতে পাশাপাশি থাকে:

  1. এক তাক সবারDjango 4.2সব কিছু একসাথে
  2. আপগ্রেডDjango 5.2পুরোনোটি ভাঙল
  3. দুটি তাক.venv × 2প্রতিটি ফোল্ডারে একটি
  4. আলাদা ইনস্টল4.2 | 5.2যার যা দরকার
  5. দুটিই চলেপাশাপাশিকেউ কাউকে ছোঁয় না
সিস্টেমের Pythonএকটিমাত্র site-packages
Python314/Lib/site-packages/
├── django/            (4.2)   ← library-project এর জন্য
└── requests/
library-project আর shop-project দুটিই এখান থেকে নেয়
Terminalনতুন কাজের জন্য আপগ্রেড
$ pip install --upgrade django
Successfully installed django-5.2.x
$ cd library-project && python manage.py runserver
ImportError: cannot import name 'utc' from 'django.utils.timezone'
দুটি প্রজেক্ট-ফোল্ডারpython -m venv .venv — দুই জায়গায়
library-project/
├── manage.py
└── .venv/     ← নিজের python + নিজের site-packages
shop-project/
├── manage.py
└── .venv/
প্রতিটি venv-এর ভেতরেএকই নাম, ভিন্ন version
library-project/.venv/.../site-packages/django  (4.2.x)
shop-project/.venv/.../site-packages/django     (5.2.x)
Terminalযে venv চালু, সেই version
(library-project) $ python -m django --version
4.2.x
(shop-project) $ python -m django --version
5.2.x
  1. এক তাক, সবার। শুরুতে নাদিয়া সব package সিস্টেমের Python-এই ইনস্টল করতেন। সেখানে site-packages ফোল্ডার একটিই, তাই প্রতিটি package-এর একটিমাত্র version থাকতে পারে। দুটি প্রজেক্ট একই Django ব্যবহার করছিল — যতক্ষণ দুটির চাহিদা মেলে, কোনো সমস্যা চোখে পড়ে না।
  2. আপগ্রেডে পুরোনোটি ভাঙল। নতুন প্রজেক্টের জন্য Django 5.2 ইনস্টল করতেই পুরোনো 4.2 সরে গেল — একই ফোল্ডারে দুটি থাকতে পারে না। পুরোনো প্রজেক্ট এমন একটি নাম ব্যবহার করত, যা Django 5.0-এ সরানো হয়েছে। কোড না বদলেও প্রজেক্ট ভাঙল, কারণ তার নিচের মাটি বদলেছে।
  3. প্রতিটি প্রজেক্টে একটি venv. দুটি ফোল্ডারেই python -m venv .venv চালানো হলো। প্রতিটি .venv একটি খালি তাক — নিজস্ব python আর নিজস্ব site-packages. venv-এর নাম ফোল্ডার থেকে আসে, তাই প্রম্পটে প্রজেক্টের নামও দেখা যায়।
  4. যার যা দরকার, তা-ই ইনস্টল। library-project-এর venv চালু করে django~=4.2, আর shop-project-এর venv-এ django~=5.2 ইনস্টল হলো। একই নামের package, কিন্তু দুটি আলাদা ফোল্ডারে — কেউ কাউকে সরায় না। সিস্টেমের Python যেমন ছিল, তেমনই থাকল।
  5. দুটিই চলে। যে প্রজেক্টের venv চালু, python সেটির ভেতরেরটি, তাই Django-ও সেটির। এখন নাদিয়া একটি আপডেট করলে অন্যটির কিছুই বদলায় না। আর venv নষ্ট হয়ে গেলে? ফোল্ডারটি মুছে আবার বানালেই হলো — venv ফেলে দেওয়ার মতো জিনিস, তাকের তালিকাটিই আসল সম্পদ (অংশ ৫)।
মূল ধারণা

এক প্রজেক্ট = এক virtual environment. venv কোনো নতুন Python ইনস্টল নয়, আলাদা কোনো ভার্চুয়াল কম্পিউটারও নয় — শুধু একটি ফোল্ডার, যেখানে ওই প্রজেক্টের package থাকে। তাই এটি তৈরি করা সস্তা, মুছে ফেলাও নিরাপদ।

তৈরি ও চালু করা — আপনার যন্ত্র অনুযায়ী

প্রজেক্টের ফোল্ডারে terminal খুলে (VS Code-এ Ctrl+`) প্রথমে venv তৈরি করুন। -m venv মানে “এই Python-এর সাথে আসা venv module চালাও”, আর শেষের .venv হলো যে ফোল্ডার তৈরি হবে। যে Python দিয়ে venv বানাবেন, venv সেই version-এরই হবে।

py -m venv .venv              # অথবা: python -m venv .venv
.venv\Scripts\Activate.ps1    # চালু করা
deactivate                    # বন্ধ করা

প্রথমবার অনেক Windows কম্পিউটারে এই লাল বার্তা আসে:

.venv\Scripts\Activate.ps1 cannot be loaded because running scripts is disabled on this system.

এটি PowerShell-এর execution policy — একটি নিরাপত্তা-সেটিং, যা স্ক্রিপ্ট চালানো আটকায়। নিজের অ্যাকাউন্টের জন্য, admin অধিকার ছাড়াই, স্থানীয় স্ক্রিপ্ট চালানোর অনুমতি দিতে একবার এটি দিন:

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

অফিস বা ল্যাবের কম্পিউটারে নীতি বদলানো নিষেধ হলে নিচের Command Prompt পথটি নিন, অথবা activate না করেই সরাসরি .venv\Scripts\python.exe চালান — ফল একই।

py -m venv .venv
.venv\Scripts\activate.bat
deactivate

পুরোনো Command Prompt-এ execution policy-র ঝামেলা নেই, কারণ এখানে চলে .bat ফাইল। Git Bash ব্যবহার করলে লিখবেন source .venv/Scripts/activate.

python3 -m venv .venv
source .venv/bin/activate
deactivate

Ubuntu বা Debian-এ ensurepip is not available ধরনের বার্তা এলে আগে OS-এর package দিন: sudo apt install python3-venv. এটি OS-এর নিজের package — এখানে sudo ঠিক আছে, কারণ আপনি apt-কে দিয়ে সিস্টেম সাজাচ্ছেন, pip দিয়ে নয়।

চালু হলে প্রম্পটের শুরুতে বন্ধনীতে venv-এর নাম আসে — যেমন (.venv) PS C:\shop-project>. এটিই সবচেয়ে সহজ চিহ্ন যে আপনি তাকের ভেতরে আছেন। Windows আর Mac/Linux-এ ভেতরের গঠন সামান্য আলাদা:

জিনিসWindowsMac / Linux
venv-এর python.venv\Scripts\python.exe.venv/bin/python
activate স্ক্রিপ্ট.venv\Scripts\.venv/bin/
package-এর তাক.venv\Lib\site-packages\.venv/lib/python3.x/site-packages/
কোন Python থেকে তৈরি.venv/pyvenv.cfg — ভেতরে home = ... লাইনে আসল Python-এর ঠিকানা

venv সরানো বা কপি করা যায় না। venv-এর ভেতরে পুরো পথ (path) লেখা থাকে। প্রজেক্ট-ফোল্ডারের নাম বদলালে, অন্য জায়গায় সরালে বা অন্য কম্পিউটারে কপি করলে venv প্রায়ই ভাঙে। নিয়ম সহজ: venv পুরোনো হলে বা ভাঙলে ফোল্ডারটি মুছে নতুন করে বানান, তারপর তালিকা থেকে package আবার ইনস্টল করুন। Python-এর বড় version বদলালেও (3.13 → 3.14) একই কাজ।

৩ · Activate আসলে কী করে — PATH-এর খেলা

“Activate” শব্দটি শুনে মনে হয় ভেতরে বড় কোনো জাদু ঘটছে। আসলে ঘটে খুব ছোট একটি কাজ। পর্ব ০৩-এ দেখেছেন, terminal-এ python লিখলে shell PATH নামের ফোল্ডার-তালিকা ধরে ক্রমানুসারে খোঁজে, আর প্রথম মিলেই থামে। Activation শুধু এই তালিকার একেবারে শুরুতে venv-এর bin (Windows-এ Scripts) ফোল্ডারটি বসিয়ে দেয়। ফলে python আর pip লিখলে সবার আগে venv-এর ভেতরেরগুলো পাওয়া যায়। ধাপে ধাপে দেখুন (উদাহরণটি Linux-এর, Windows-এও হুবহু একই ঘটনা):

1$ which python32$ source .venv/bin/activate3(.venv) $ which python4(.venv) $ python -m pip install rich5(.venv) $ deactivate
এই terminal-এর অবস্থা
PATH-এর প্রথম ঘর
python মানে
VIRTUAL_ENV
rich কোথায়
  1. Activate-এর আগে। রাফি তাঁর shop প্রজেক্টের ফোল্ডারে আছেন। which python3 (Windows-এ where.exe python) দেখাচ্ছে সিস্টেমের Python — কারণ PATH-এর শুরুতে /usr/bin. এখন pip দিয়ে কিছু ইনস্টল করলে সেটি সিস্টেমের তাকে যেত।
  2. Activate. source .venv/bin/activate একটি ছোট shell-স্ক্রিপ্ট চালায়। এটি তিনটি কাজ করে: PATH-এর শুরুতে .venv/bin বসায়, VIRTUAL_ENV নামে একটি environment variable-এ venv-এর ঠিকানা রাখে, আর প্রম্পটে (.venv) যোগ করে। ব্যস — কোনো ফাইল কপি হয়নি, কিছু ইনস্টলও হয়নি।
  3. এখন python মানে venv-এর python. একই নাম লিখলেও shell এবার PATH-এর প্রথম ঘর .venv/bin-এই python পেয়ে থেমে গেল। এই python চালু হয়েই pyvenv.cfg ফাইল দেখে বোঝে যে সে একটি venv-এর অংশ, তাই package খোঁজে venv-এর site-packages-এ।
  4. pip-ও venv-এর। python -m pip install rich — যেহেতু python এখন venv-এর, pip-ও তারই, আর rich গিয়ে বসল .venv-এর তাকে। সিস্টেমের Python কিছুই জানল না। (rich হলো terminal-এ রঙিন লেখা ছাপার একটি জনপ্রিয় package.)
  5. Deactivate. deactivate PATH আর প্রম্পট আগের অবস্থায় ফিরিয়ে দেয়। কিন্তু rich মুছে যায়নি — সেটি এখনো .venv-এর তাকে আছে, পরের বার activate করলেই পাবেন। মনে রাখুন, activation শুধু এই একটি terminal-জানালার ব্যাপার; নতুন জানালা খুললে আবার activate করতে হয়।
Activate = PATH-এর শুরুতে .venv/bin
তাই python আর pip → venv-এর ভেতরেরটি

এর একটি দারুণ ফল আছে: activate না করেও venv ব্যবহার করা যায়। সরাসরি .venv/bin/python app.py (Windows-এ .venv\Scripts\python.exe app.py) চালালে সেই python নিজেই জানে সে কোন venv-এর। সার্ভারে, cron-এ বা অটোমেশন স্ক্রিপ্টে সাধারণত এভাবেই চালানো হয় — সেখানে কেউ activate করে না।

আমি কি venv-এর ভেতরে? — কোড দিয়ে যাচাই

প্রম্পট দেখে বোঝা যায়, কিন্তু প্রোগ্রামের ভেতর থেকেও প্রশ্নটির উত্তর পাওয়া যায়। Python-এর দুটি মান আছে: sys.prefix (এখন যে পরিবেশ চলছে তার মূল ফোল্ডার) আর sys.base_prefix (আসল Python যেখানে ইনস্টল করা)। venv-এর ভেতরে দুটি আলাদা হয়ে যায়। চালিয়ে দেখুন:

import sys
import sysconfig
from pathlib import Path

# venv-এর ভেতরে থাকলে sys.prefix বদলে যায়, base_prefix থাকে আসল Python-এ
in_venv = sys.prefix != sys.base_prefix
print("venv-এর ভেতরে?", in_venv)

# pip যে ফোল্ডারে package বসায়, তার নাম
site_dir = Path(sysconfig.get_paths()["purelib"])
print("package-এর ফোল্ডার:", site_dir.name)
print("এই ফোল্ডার import-এর খোঁজার তালিকায়?", str(site_dir) in sys.path)
print("সব Python-এ venv module আছে?", "venv" in sys.stdlib_module_names)

এই পাতায় “চালান” চাপলে কোডটি একটি অনলাইন সার্ভারের সাধারণ Python-এ চলে, কোনো venv-এ নয় — তাই প্রথম লাইন আসবে False. নিজের কম্পিউটারে venv activate করে চালালে আসবে True. দ্বিতীয় লাইন দেখাবে site-packages — pip যে তাকে package রাখে। তৃতীয় লাইন True: সেই ফোল্ডার sys.path-এ আছে, অর্থাৎ import এখানেও খোঁজে (পর্ব ২২)। শেষ লাইনও True, কারণ venv Python 3.3 থেকে standard library-র অংশ। ঠিকানাগুলো পুরো দেখতে চাইলে নিজের কম্পিউটারে print(sys.prefix) আর print(site_dir) যোগ করুন — সেগুলো যন্ত্রভেদে আলাদা, তাই এখানে রাখা হয়নি।

VS Code আর git — দুটি জরুরি অভ্যাস

  • VS Code-কে venv দেখিয়ে দিন। প্রজেক্ট-ফোল্ডারে .venv থাকলে VS Code সাধারণত নিজেই সেটি চিনে প্রস্তাব দেয়। না দিলে Ctrl+Shift+P → “Python: Select Interpreter” → .venv-এর python বাছুন। নিচের status bar-এ ('.venv') দেখলে নিশ্চিত। তারপর নতুন terminal খুললে VS Code নিজেই activate করে দেয়। “Python: Create Environment” কমান্ড দিয়ে VS Code থেকেই venv বানানো যায়।
  • .venv কখনো git-এ নয়। এর ভেতরে শত শত মেগাবাইট ফাইল, আর সেগুলো আপনার OS ও আপনার ফোল্ডার-পথের সাথে বাঁধা — অন্য কারও কম্পিউটারে চলবেই না। .gitignore ফাইলে .venv/ লিখে দিন। Python 3.13 থেকে python -m venv নিজেই venv-এর ভেতরে একটি .gitignore রেখে দেয়, তবু প্রজেক্টের নিজের .gitignore-এ লাইনটি রাখা ভালো অভ্যাস। git-এ যাবে শুধু package-এর তালিকা — পরের অংশগুলোতে সেটিই দেখব।
# .gitignore — Python প্রজেক্টের ন্যূনতম
.venv/
__pycache__/
*.pyc
.env          # পাসওয়ার্ড, API key — কখনো git-এ নয়

৪ · pip — PyPI থেকে আপনার তাকে

venv চালু করার পর এবার package আনার পালা। pip-এর পাঁচটি কমান্ড দিয়েই দৈনন্দিন প্রায় সব কাজ চলে:

python -m pip install requests        # ইনস্টল (সাথে তার dependency-ও)
python -m pip install --upgrade requests   # নতুন version-এ আপগ্রেড
python -m pip uninstall requests      # সরানো (তার dependency থেকে যায়)
python -m pip list                    # এই পরিবেশে কী কী আছে
python -m pip show requests           # একটি package-এর বিস্তারিত
python -m pip freeze                  # সব package, হুবহু version-সহ

প্রতিটি কমান্ডের শুরুতে python -m pip কেন, শুধু pip কেন নয় — পর্ব ০৩-এ এর কারণ দেখেছেন: -m pip মানে “এই python-এর pip চালাও”, তাই package ঠিক সেই Python-এ যায়, যেটি দিয়ে কোড চলবে। venv চালু থাকলে pip আর python -m pip একই জিনিস, তবু অভ্যাসটি রাখলে কখনো ভুল Python-এ ইনস্টল হবে না। Windows-এ venv-এর বাইরে py -m pip.

pip show-এর ফল পড়তে শিখুন — সমস্যা খোঁজার সময় খুব কাজে লাগে:

(.venv) $ python -m pip show requests
Name: requests
Version: 2.32.3
Summary: Python HTTP for Humans.
Location: /home/rafi/shop/.venv/lib/python3.14/site-packages
Requires: certifi, charset-normalizer, idna, urllib3
Required-by:

Location বলে package কোন তাকে আছে — venv-এর ভেতরে, নাকি ভুল করে সিস্টেমে। Requires হলো এর নিজের dependency, আর Required-by বলে আপনার আর কোন package এর ওপর নির্ভর করে — সরানোর আগে এটি দেখে নিন। এবার দেখা যাক pip install requests লিখে Enter চাপলে ভেতরে কী কী ঘটে:

  1. কমান্ডinstall requestsকোন python-এর pip?
  2. খোঁজাpypi.org/simpleকোন ফাইলগুলো আছে
  3. ResolveRequires-Distdependency-র হিসাব
  4. নামানো*.whl + hashwheel বাছাই
  5. বসানোsite-packagesচালু venv-এর তাকে
Terminalvenv চালু আছে — প্রম্পটে (.venv)
(.venv) $ python -m pip install requests
python = .venv/bin/python → pip = এই venv-এর pip
PyPI-র তালিকাপ্রতিটি version-এর সব ফাইল
requests-2.32.3.tar.gz                (sdist)
requests-2.32.3-py3-none-any.whl      (wheel)
requests-2.32.2.tar.gz
… পুরোনো version-গুলো …
requests-এর metadataএরও dependency আছে
Requires-Python: >=3.8
Requires-Dist: charset_normalizer<4,>=2
Requires-Dist: idna<4,>=2.5
Requires-Dist: urllib3<3,>=1.21.1
Requires-Dist: certifi>=2017.4.17
Terminalপাঁচটি wheel নামানো হলো
Collecting requests
  Downloading requests-2.32.3-py3-none-any.whl
Collecting charset-normalizer<4,>=2 (from requests)
Collecting idna<4,>=2.5 (from requests)
Collecting urllib3<3,>=1.21.1 (from requests)
Collecting certifi>=2017.4.17 (from requests)
.venv/lib/python3.14/site-packages/কোড + রসিদ
requests/                  ← import requests এটাই পায়
requests-2.32.3.dist-info/ ← নাম, version, ফাইলের তালিকা
urllib3/  idna/  certifi/  charset_normalizer/
Successfully installed certifi-… charset-normalizer-… idna-… requests-2.32.3 urllib3-…
  1. কোন pip, কোন তাক? প্রথম প্রশ্ন সবসময় এটিই। venv চালু, তাই python মানে venv-এর python, আর -m pip মানে তারই pip. অর্থাৎ শেষে সবকিছু এই venv-এর site-packages-এ যাবে। ভুল terminal-এ (venv ছাড়া) একই কমান্ড দিলে package যেত অন্য তাকে।
  2. PyPI-তে খোঁজা। pip PyPI-র “simple index”-এ requests-এর পাতা চায়, যেখানে প্রতিটি version-এর সব ফাইলের তালিকা থাকে। কোনো version-শর্ত না দেওয়ায় pip সবচেয়ে নতুন স্থিতিশীল version নেয়, যেটি আপনার Python version-এ চলে। একই version-এর wheel আর sdist দুটোই থাকলে pip wheel-কে অগ্রাধিকার দেয়।
  3. Dependency resolve করা। requests নিজেও চারটি package-এর ওপর নির্ভর করে — এগুলো আপনার transitive dependency. প্রত্যেকের version-শর্ত আছে (urllib3<3,>=1.21.1). pip-এর resolver এমন একটি সেট খোঁজে, যা আগে থেকে ইনস্টল থাকা সবকিছুর শর্তও মানে। মিল না পেলে এখানেই থেমে conflict-এর error দেয়।
  4. wheel নামানো ও যাচাই। প্রতিটির জন্য আপনার Python আর OS-এর সাথে মেলে এমন wheel বাছাই হয় — বিশুদ্ধ Python হলে py3-none-any. PyPI প্রতিটি ফাইলের hash দেয়; নামানো ফাইল সেই hash-এর সাথে মিলিয়ে দেখা যায় যে পথে কেউ বদলায়নি। (pip ডাউনলোড cache-এও রাখে, পরের venv-এ আবার নামাতে হয় না।)
  5. তাকে বসানো। wheel একটি zip — pip সেটি খুলে কোড বসায় site-packages-এ, পাশে রাখে একটি .dist-info ফোল্ডার: নাম, version আর কোন কোন ফাইল বসানো হয়েছে তার রসিদ। pip list, pip show, pip uninstall সবাই এই রসিদ পড়েই কাজ করে। এবার import requests লিখলে Python sys.path-এর এই ফোল্ডারে package-টি পাবে।

দুটি জিনিস লক্ষ করুন। এক: আপনি একটি package চেয়েছেন, এসেছে পাঁচটি। বড় প্রজেক্টে সরাসরি dependency দশটি হলে transitive মিলিয়ে একশোও হতে পারে। দুই: pip uninstall requests শুধু requests সরায়, তার আনা চারটি থেকে যায় — pip জানে না সেগুলো আর কেউ ব্যবহার করে কি না। পরিষ্কার করতে চাইলে সবচেয়ে সহজ পথ: venv মুছে নতুন করে বানানো।

যে উদাহরণ আসলে দেখা যায়: এই অংশের version নম্বরগুলো (requests 2.32.3 ইত্যাদি) বোঝানোর জন্য — আপনি যখন ইনস্টল করবেন, তখন হয়তো নতুন version আসবে। কোন version এসেছে, তা pip show বা pip list দিয়ে সবসময় নিজে দেখে নিন।

৫ · Version-এর ভাষা আর requirements.txt

“requests লাগবে” বলা যথেষ্ট নয় — কোন requests? নাদিয়ার গল্প মনে করুন: Django 4.2 আর 5.2 একই নামের দুটি ভিন্ন জিনিস। তাই dependency-র সাথে version-শর্ত লেখা হয়, যাকে বলে version specifier. নিয়মগুলো PEP 440-এ লেখা:

লেখাঅর্থকখন
requests==2.32.3হুবহু এই versionচালানোর উপযোগী অ্যাপ, হুবহু পুনরুৎপাদন
requests>=2.312.31 বা তার যেকোনো নতুনন্যূনতম চাহিদা জানাতে; ভবিষ্যতের বড় version-এ ভাঙার ঝুঁকি
django~=4.2অন্তত 4.2, কিন্তু 5.0-এর নিচে (4.x)“সামঞ্জস্যপূর্ণ আপডেট নেব, বড় বদল নয়”
django~=4.2.0অন্তত 4.2.0, কিন্তু 4.3-এর নিচে (4.2.x)শুধু বাগ-ঠিকের আপডেট
numpy>=1.26,<2দুটি শর্ত একসাথে (কমা মানে “এবং”)জানা কোনো ভাঙন এড়াতে
requests (শর্ত ছাড়া)যা সবচেয়ে নতুনশুধু খেলার ছলে পরীক্ষায়

~= চিহ্নটির নাম “compatible release”, আর এটি বোঝার জন্য version নম্বরের প্রথা জানা দরকার। অনেক package semantic versioning-এর মতো চলে: MAJOR.MINOR.PATCH. পুরোনো কোড ভাঙে এমন বদলে MAJOR বাড়ে (4 → 5), নতুন সুবিধায় MINOR (4.1 → 4.2), শুধু বাগ ঠিকে PATCH (4.2.10 → 4.2.11). তবে এটি প্রতিশ্রুতি নয়, প্রথা — সব package কঠোরভাবে মানে না, Django-র নিজের নীতিও কিছুটা আলাদা। তাই শর্ত দিয়ে আশা করা যায়, নিশ্চিত হওয়া যায় না।

শর্তগুলো আসলে ছোট তুলনা মাত্র। নিচের কোডে সাধারণ string আর tuple দিয়ে ==, >= আর ~=-এর যুক্তি নিজে লিখে দেখা হয়েছে (আসল pip এর চেয়ে অনেক বেশি নিয়ম মানে — যেমন 2.0rc1-এর মতো version):

def ver(text):
    return tuple(int(part) for part in text.split("."))

def satisfies(installed, spec):
    for op in ("==", ">=", "~="):
        if op in spec:
            want = spec.partition(op)[2]
            break
    v, w = ver(installed), ver(want)
    if op == "==":
        return v == w
    if op == ">=":
        return v >= w
    # ~=4.2 মানে: অন্তত 4.2, কিন্তু শেষ অংশ ছাড়া বাকিটা মিলতে হবে (4.x)
    return v >= w and v[:len(w) - 1] == w[:-1]

checks = [("django~=4.2", "4.2.11"), ("django~=4.2", "5.0.3"),
          ("requests>=2.31", "2.32.3"), ("numpy==1.26.4", "2.1.0")]
for spec, installed in checks:
    result = "মেলে" if satisfies(installed, spec) else "মেলে না"
    print(f"{spec:<15} | ইনস্টল আছে {installed:<7} -> {result}")

ver() "4.2.11"-কে (4, 2, 11) tuple বানায়, আর tuple-এর তুলনা বাঁ দিক থেকে ঘর ধরে ধরে হয় (পর্ব ১৫)। তাই (4, 2, 11) >= (4, 2) সত্য। ~=-এর জন্য বাড়তি শর্ত: শেষ অংশ বাদে বাকিটা মিলতে হবে — ~=4.2-এ শুধু প্রথম অংশ 4. ফল চার লাইনে: Django 4.2.11 → মেলে; Django 5.0.3 → মেলে না (MAJOR বদলেছে — নাদিয়ার পুরোনো প্রজেক্ট ঠিক এখানেই রক্ষা পেত); requests 2.32.3 → মেলে; numpy 2.1.0 → মেলে না (হুবহু 1.26.4 চাওয়া হয়েছিল)। বাস্তবে এ কাজ নিজে লিখবেন না — packaging নামের package-টি PEP 440-এর পুরো নিয়ম মানে, pip নিজেও ভেতরে সেটিই ব্যবহার করে।

requirements.txt — তাকের তালিকা

রাফি তাঁর দলের সঙ্গী তৃষাকে প্রজেক্টটি দিতে চান। .venv পাঠানো চলবে না (অংশ ২)। পাঠাতে হবে তাকের তালিকা, যাতে তৃষা নিজের কম্পিউটারে একই তাক সাজাতে পারেন। সবচেয়ে প্রচলিত তালিকার নাম requirements.txt — এক লাইনে একটি package. নামটি প্রথা, নিয়ম নয়, কিন্তু সবাই এটিই খোঁজে।

# রাফি: এখনকার venv-এর সব package, হুবহু version-সহ, ফাইলে
python -m pip freeze > requirements.txt

# তৃষা: git clone করার পর নিজের কম্পিউটারে
python -m venv .venv
.venv\Scripts\Activate.ps1          # Mac/Linux: source .venv/bin/activate
python -m pip install -r requirements.txt

pip freeze প্রতিটি ইনস্টল করা package name==version আকারে লেখে — transitive dependency-সহ। ফাইলটি দেখতে এমন:

certifi==2024.8.30
charset-normalizer==3.4.0
idna==3.10
requests==2.32.3
urllib3==2.2.3

এখানে একটি সূক্ষ্ম সমস্যা আছে। রাফি শুধু requests চেয়েছিলেন, কিন্তু তালিকায় পাঁচটি — কোনটি তাঁর সিদ্ধান্ত আর কোনটি শুধু সঙ্গে এসেছে, ফাইল দেখে বোঝা যায় না। বছরখানেক পরে requests বাদ দিলে বাকি চারটি অকারণে থেকে যাবে। তাই অভিজ্ঞরা প্রায়ই দুটি স্তর রাখেন: হাতে লেখা ছোট তালিকা (“আমি কী চাই” — যেমন requests>=2.31), আর সেখান থেকে টুল দিয়ে তৈরি সম্পূর্ণ, হুবহু তালিকা (“ঠিক কী ইনস্টল হবে”)। দ্বিতীয়টিই lock file-এর ধারণা — অংশ ৮-এ আসছে।

মূল ধারণা

Pinning মানে version বেঁধে দেওয়া। শর্ত ছাড়া requests লিখলে আজ আর ছয় মাস পরে ভিন্ন version আসবে — আজ চলা প্রজেক্ট কাল কোনো কারণ ছাড়াই ভাঙতে পারে। চালানোর উপযোগী অ্যাপ (ওয়েবসাইট, স্ক্রিপ্ট, AI মডেলের পরীক্ষা) হুবহু pin করে রাখুন, আর আপডেট করুন নিজের সিদ্ধান্তে, পরীক্ষা করে। একে বলে reproducibility — একই তালিকা থেকে যেকোনো জায়গায় একই পরিবেশ।

৬ · pyproject.toml — প্রজেক্টের পরিচয়পত্র

requirements.txt শুধু একটি package-তালিকা। কিন্তু প্রজেক্টের আরও পরিচয় আছে: তার নাম কী, version কত, কোন Python লাগে, terminal-এ কোন কমান্ড দিয়ে চালানো যায়, কোন টুলের কী সেটিং। এসব রাখার আধুনিক মানক জায়গা হলো pyproject.toml — প্রজেক্ট-ফোল্ডারের মূলে একটি ফাইল, যা TOML ভাষায় লেখা। পর্ব ২১-এর মাঝপথের প্রজেক্টের মতো একটি বাজার-হিসাবের CLI টুলের জন্য:

# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "bazar-hisab"
version = "0.1.0"
description = "বাজারের খরচ হিসাব রাখার একটি CLI টুল"
requires-python = ">=3.10"
dependencies = [
    "rich~=13.7",
    "requests>=2.31",
]

[project.scripts]
bazar = "bazar_hisab.cli:main"
  • [build-system] (PEP 518): এই প্রজেক্ট থেকে wheel বানাতে কোন টুল লাগবে। এই টুলকে বলে build backend — hatchling, setuptools, flit-core ইত্যাদি। pip নিজে build করে না; এই অংশ পড়ে backend-কে ডেকে আনে।
  • [project] (PEP 621): নাম, version, বিবরণ, requires-python আর dependencies — সব টুলের বোঝার মতো মানক ঘর। পুরোনো দিনে এসব থাকত setup.py নামের একটি Python স্ক্রিপ্টে।
  • requires-python: পুরোনো Python-এ কেউ ইনস্টল করতে চাইলে pip সঙ্গে সঙ্গে থামিয়ে দেবে — অদ্ভুত error-এর বদলে পরিষ্কার বার্তা।
  • [project.scripts]: ইনস্টলের পর terminal-এ bazar লিখলেই bazar_hisab/cli.py-এর main() চলবে। pip venv-এর bin/Scripts-এ এই নামে ছোট একটি চালানোর ফাইল বানিয়ে দেয়।

খেয়াল করুন, এখানে dependency-গুলো তুলনামূলক ঢিলে শর্তে (~=, >=) লেখা। কারণ pyproject.toml বলে “এই প্রজেক্টের কী লাগে”, “ঠিক কোন version ইনস্টল হবে” নয়। হুবহু তালিকার কাজ lock file-এর।

নিজের প্রজেক্ট নিজেই ইনস্টল — editable mode আর src layout

বড় প্রজেক্টে প্রস্তাবিত সাজানো হলো src layout — import করা যায় এমন package-টি থাকে src/-এর ভেতরে:

bazar-hisab/
├── pyproject.toml
├── README.md
├── src/
│   └── bazar_hisab/
│       ├── __init__.py
│       └── cli.py
└── tests/
    └── test_cli.py

এভাবে রাখলে প্রজেক্টের মূল ফোল্ডার থেকে python চালালেও import bazar_hisab “ভুলবশত” কাজ করে না — package-টি সত্যিই ইনস্টল করা থাকতে হয়। শুনতে ঝামেলা মনে হলেও এটিই সুবিধা: আপনার কম্পিউটারে যা চলে, ইনস্টলের পর ব্যবহারকারীর কম্পিউটারেও তা-ই চলবে। কিন্তু প্রতিবার কোড বদলে আবার ইনস্টল? তার সমাধান editable install:

(.venv) $ python -m pip install -e .     # . মানে "এই ফোল্ডারের প্রজেক্ট"
(.venv) $ bazar --help                   # [project.scripts]-এর কমান্ড এখন চলে

-e (editable) দিলে pip site-packages-এ আপনার কোডের কপি বসায় না, বসায় আপনার src/ ফোল্ডারের দিকে একটি নির্দেশ। এখন cli.py-তে কিছু বদলালেই পরের বার চালানোয় সেটি দেখা যায়। সঙ্গে dependencies-এর তালিকাও ইনস্টল হয়ে যায়। তৃষা git clone করে শুধু venv বানিয়ে pip install -e . দিলেই পুরো প্রজেক্ট প্রস্তুত। পর্ব ৩৯-এ pytest দিয়ে টেস্ট লেখার সময় এই সাজানো খুব কাজে লাগবে।

৭ · আধুনিক টুল আর “externally-managed-environment”

venv আর pip Python-এর সাথেই আসে, আর সব জায়গায় চলে — এই পর্বের ভিত এ দুটিই। তবে বাস্তবে আরও কিছু টুল দেখবেন। কোনটি কেন আছে, জানা থাকলে বিভ্রান্তি কমে:

⚡ uv

Astral কোম্পানির Rust-এ লেখা টুল (২০২৪)। venv তৈরি, ইনস্টল, lock file, এমনকি Python নিজে ইনস্টল — এক টুলে, আর খুব দ্রুত। uv pip install pip-এর মতো চলে; uv add, uv lock, uv run দিয়ে পুরো প্রজেক্ট সামলায়। দ্রুত জনপ্রিয় হচ্ছে, তবে এটি PSF-এর নয়, একটি কোম্পানির পণ্য — বিনামূল্যের ও open source.

uv · uv.lock

🧰 pipx

Terminal-এ চালানোর Python টুল (যেমন কোড সাজানোর black) ইনস্টলের জন্য। প্রতিটি টুল নিজের আলাদা venv-এ থাকে, কিন্তু কমান্ডটি যেকোনো ফোল্ডার থেকে চলে। কোনো প্রজেক্টের অংশ নয় এমন টুলের জন্য আদর্শ।

pipx install black

📜 Poetry ও PDM

Poetry (২০১৮) আর PDM — dependency যোগ, resolve, lock file আর PyPI-তে প্রকাশ এক টুলে। অনেক পুরোনো ও প্রতিষ্ঠিত প্রজেক্টে দেখবেন। দুটিই এখন pyproject.toml-এর মানক [project] অংশ পড়তে পারে।

poetry.lock · pdm.lock

🧪 conda

ডেটা সায়েন্সে জনপ্রিয়। শুধু Python package নয় — C/C++ লাইব্রেরি, CUDA, এমনকি R-ও ইনস্টল করে। PyPI নয়, নিজস্ব channel (conda-forge ইত্যাদি) থেকে আনে। Python-এর বাইরের ভারী dependency থাকলে conda জীবন সহজ করে।

environment.yml
# একই কাজ uv দিয়ে — তুলনার জন্য
uv venv                    # .venv তৈরি
uv pip install requests    # pip-এর মতো ইনস্টল
uv add requests            # pyproject.toml-এ যোগ + uv.lock আপডেট
uv run python app.py       # ঠিক venv দিয়ে চালানো, activate ছাড়াই

কোনটি বাছবেন? শেখার সময় venv + pip — এগুলো সবখানে আছে, আর অন্য সব টুল এই ধারণার ওপরেই দাঁড়িয়ে। দল বা চাকরিতে গেলে সেখানে যা চলে, সেটি। আর ABCL TECH-এর AI-এর জন্য Python কোর্সের venv পর্বে conda আর uv-এর আরও ব্যবহারিক তুলনা আছে। টুল বদলায়, কিন্তু চারটি ধারণা একই থাকে: আলাদা পরিবেশ, version-শর্ত, তালিকা আর lock.

“externally-managed-environment” — OS যখন বলে “আমার Python-এ হাত দেবেন না”

নতুন Linux (যেমন Debian 12, Ubuntu 23.04 থেকে) বা Mac-এর Homebrew Python-এ venv ছাড়া pip install দিলে এমন বার্তা আসে:

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install. ... you may create a virtual environment ...

এটি ভুল নয়, ইচ্ছাকৃত সুরক্ষা — PEP 668. Linux-এর অনেক সিস্টেম-টুল Python-এ লেখা, আর সেগুলো OS-এর apt দিয়ে আনা নির্দিষ্ট version-এর package-এর ওপর নির্ভর করে। আপনি pip দিয়ে সেই একই তাকে কোনো package-এর ভিন্ন version বসালে OS-এর টুল ভাঙতে পারে। তাই OS তার Python-কে “externally managed” ঘোষণা করে, আর pip সেখানে লিখতে অস্বীকার করে। সমাধানের পথ:

  • প্রজেক্টের জন্য: venv বানান — ঠিক এই পর্বে যা শিখলেন। বার্তাটি আসলে এটাই বলছে।
  • Terminal-টুলের জন্য: pipx install ...
  • OS-এর নিজের প্রয়োজনে: sudo apt install python3-requests-এর মতো OS-এর package.
  • যা করবেন না: sudo pip install বা --break-system-packages. দ্বিতীয়টির নামেই সতর্কবার্তা — “সিস্টেমের package ভাঙুক”। AI প্রায়ই এটি বলে দেয় (অংশ ৯)।

বাস্তব উদাহরণ: admin অধিকার ছাড়া ল্যাবের কম্পিউটার

রাকিব একটি প্রশিক্ষণ কেন্দ্রের শেয়ার করা কম্পিউটারে কাজ করেন, admin অধিকার নেই। pip install pandas দিতেই আসে Permission denied — কারণ সিস্টেমের Python-এর ফোল্ডারে লেখার অনুমতি তাঁর নেই। এখানে venv-ই সবচেয়ে পরিষ্কার সমাধান: venv তৈরি হয় রাকিবের নিজের ফোল্ডারে, যেখানে তাঁর লেখার অধিকার আছে, তাই কোনো admin অধিকার লাগে না, অন্য কারও কাজেও হাত পড়ে না। পুরোনো বিকল্প pip install --user package-কে ব্যবহারকারীর ব্যক্তিগত ফোল্ডারে বসায়, কিন্তু সেখানেও সব প্রজেক্ট এক তাক ভাগ করে, আর PEP 668-যুক্ত সিস্টেমে এটিও আটকানো। তাই প্রজেক্টের কাজে venv. দিনের শেষে প্রজেক্ট-ফোল্ডারসহ venv মুছে দিলে পরের জনের জন্য কম্পিউটার পরিষ্কার থাকে।

৮ · Dependency hell, lock file আর package-এর নিরাপত্তা

ধরুন আপনার প্রজেক্টে দুটি package: একটি চায় urllib3<2, অন্যটি চায় urllib3>=2. দুটির শর্ত একসাথে পূরণ করা অসম্ভব — একটি venv-এ একটি package-এর একটিই version থাকতে পারে। এমন জট আর তার পরের দিনভর “কোন version মেলে” খোঁজার নাম dependency hell. pip তখন সোজাসুজি বলে দেয়:

ERROR: Cannot install pkg-a and pkg-b because these package versions have conflicting dependencies.
ERROR: ResolutionImpossible

pip 20.3 (২০২০) থেকে নতুন resolver এমন জট ইনস্টলের আগেই ধরে। আগে pip প্রায়ই চুপচাপ একটি ভুল সেট ইনস্টল করে ফেলত, আর ভাঙন ধরা পড়ত পরে। সমাধানের সাধারণ পথ: কোনো একটি package-এর অন্য version খোঁজা (হয়তো নতুন version urllib3 2 সমর্থন করে), বা অপ্রয়োজনীয় package বাদ দেওয়া।

Lock file — “আমার কম্পিউটারে তো চলে” সমস্যার উত্তর

Lock file হলো resolve করার পরের সম্পূর্ণ ছবি: প্রতিটি package (transitive-সহ), তার হুবহু version, আর প্রায়ই ফাইলের hash. uv লেখে uv.lock, Poetry লেখে poetry.lock; pip-tools-এর pip-compile হাতে লেখা ছোট তালিকা থেকে hash-সহ requirements.txt বানায়। ২০২৫ সালে PEP 751 সব টুলের জন্য একটি সাধারণ lock-ফরম্যাট (pylock.toml) মেনে নিয়েছে, টুলগুলো ধীরে ধীরে তা সমর্থন করছে।

ফাইলকী বলেকে লেখে
pyproject.toml“আমার কী লাগে” — ঢিলে শর্তআপনি
requirements.txtহাতে লেখা হলে চাহিদা; pip freeze হলে হুবহু ছবি (hash ছাড়া)আপনি বা pip
lock file“ঠিক কী ইনস্টল হবে” — হুবহু version + hashটুল (uv, Poetry, pip-tools)

Package-এর নিরাপত্তা — PyPI-তে যে কেউ আপলোড করতে পারে

PyPI একটি খোলা ভাণ্ডার: অ্যাকাউন্ট খুলে যে কেউ package আপলোড করতে পারে, কেউ আগে থেকে কোড পরীক্ষা করে দেয় না। আর pip install অনেক সময় package-এর কোড আপনার কম্পিউটারে চালায়ও — বিশেষ করে sdist build করার সময়। তাই ভুল package ইনস্টল মানে অচেনা কারও কোড আপনার কম্পিউটারে, আপনার ফাইল আর পাসওয়ার্ডের কাছে চালানো। আপনি যে package-গুলোর ওপর দাঁড়িয়ে আছেন, সেখানে ক্ষতিকর কোড ঢুকিয়ে আক্রমণকে বলে supply-chain attack. সবচেয়ে সাধারণ ফাঁদগুলো:

  • Typosquatting: জনপ্রিয় নামের কাছাকাছি বানানে ক্ষতিকর package — reqeusts, python3-dateutil (আসলটি python-dateutil), jeIlyfish (ছোট হাতের l-এর জায়গায় বড় হাতের I; আসলটি jellyfish). এক অক্ষরের টাইপ-ভুলেই ফাঁদে পা।
  • Slopsquatting: AI কখনো কখনো আত্মবিশ্বাসের সাথে এমন package-নাম বলে, যা আসলে নেই। গবেষকেরা দেখিয়েছেন, এমন বানানো নাম বারবার একই রকম আসে — তাই আক্রমণকারী আগেই সেই নামে ক্ষতিকর package আপলোড করে রাখতে পারে।
  • Dependency confusion: কোনো প্রতিষ্ঠানের ভেতরের package-এর নামেই পাবলিক PyPI-তে package রাখা, যাতে ইনস্টলার ভুলটি নামায়।

AI কোনো অচেনা package-এর নাম বললে কী যাচাই করবেন, ধাপে ধাপে দেখুন:

  1. AI-এর প্রস্তাবbangla-date-utilsনামটি কি আসল?
  2. বানানঅক্ষর ধরে মেলানোtyposquat?
  3. PyPI পাতাpypi.org/project/…বয়স, repo, লেখক
  4. Pin করে ইনস্টল==x.y.zvenv-এর ভেতরে
  5. Auditpip-auditজানা ত্রুটি আছে?
AI-এর উত্তরশুনতে খুব বিশ্বাসযোগ্য
"বাংলা তারিখ রূপান্তরের জন্য এটি ইনস্টল করুন:
  pip install bangla-date-utils"
নামটি আপনি আগে কখনো শোনেননি
বানান মেলানোপরিচিত নামের সাথে
requests        ✓ আসল
reqeusts        ✗ e আর u-র জায়গা বদল
request         ✗ শেষের s নেই
beautifulsoup4  ✓ আসল   beautifulsoup ✗ পুরোনো/ভিন্ন
pypi.org-এ খুঁজে দেখাসন্দেহের চিহ্ন
✗ পাতাই নেই        → AI নাম বানিয়েছে; ইনস্টল নয়
✗ গত সপ্তাহে তৈরি, একটিমাত্র version, বিবরণ ফাঁকা
✗ source repository-র লিংক নেই
✓ বহু বছরের ইতিহাস, পরিচিত repo, নিয়মিত release
Terminalযাচাইয়ের পর, venv-এর ভেতরে
(.venv) $ python -m pip install "requests==2.32.3"
(.venv) $ python -m pip freeze > requirements.txt
Terminalজানা নিরাপত্তা-ত্রুটির খোঁজ
(.venv) $ python -m pip install pip-audit
(.venv) $ pip-audit
No known vulnerabilities found
ত্রুটি পেলে: package, version, ত্রুটির ID আর কোন version-এ ঠিক হয়েছে
  1. AI একটি নাম বলল। নামটি যুক্তিসংগত শোনায়, আর AI-এর ভাষায় কোনো দ্বিধা নেই। কিন্তু AI-এর আত্মবিশ্বাস কোনো প্রমাণ নয় — package-টি থাকতেও পারে, না-ও থাকতে পারে, আর থাকলেও সেটি কার বানানো, জানেন না। এই মুহূর্তে থামাই সবচেয়ে গুরুত্বপূর্ণ।
  2. বানান মেলান। পরিচিত package হলে অক্ষর ধরে ধরে মেলান — উল্টো অক্ষর, বাড়তি বা বাদ পড়া অক্ষর, - আর _-এর খেলা। কপি-পেস্ট করলে টাইপ-ভুল এড়ানো যায়, কিন্তু উৎসটিই ভুল হলে লাভ নেই। তাই পরের ধাপ।
  3. PyPI-র পাতা দেখুন। pypi.org-এ নামটি খুঁজুন। পাতাই না থাকলে AI নাম বানিয়েছে — ইনস্টল করবেন না, কারণ কাল কেউ সেই নামে ক্ষতিকর package রেখে দিতে পারে। পাতা থাকলে দেখুন: কবে থেকে আছে, কয়টি release, source repository-র লিংক, লেখক কে, ডকুমেন্টেশন আছে কি না। খুব নতুন, ফাঁকা বা নামহীন package-এ সতর্ক হোন।
  4. Pin করে venv-এ ইনস্টল। যাচাইয়ের পর venv-এর ভেতরে, নির্দিষ্ট version দিয়ে ইনস্টল করুন, আর তালিকায় লিখে রাখুন। Pin করা থাকলে package-এর মালিকের অ্যাকাউন্ট পরে আক্রান্ত হয়ে নতুন ক্ষতিকর version এলেও তা আপনার কাছে আপনাআপনি আসবে না।
  5. নিয়মিত audit. pip-audit আলাদা একটি টুল (pip-এর নিজের কমান্ড নয়) — এটি আপনার package-গুলো জানা নিরাপত্তা-ত্রুটির ডেটাবেসের সাথে মিলিয়ে দেখায় কোনটিতে সমস্যা আর কোন version-এ ঠিক হয়েছে। Pinning-এর উল্টো দিকও আছে: বেঁধে রাখা পুরোনো version-এ ত্রুটি ধরা পড়লে আপনাকেই আপডেট করতে হবে। এজন্য audit.

পরিচিত, বহুল ব্যবহৃত package-কে অগ্রাধিকার দিন। পাঁচ লাইনের কাজের জন্য অচেনা package না এনে নিজে লিখুন বা standard library-তে খুঁজুন — প্রতিটি নতুন dependency একটি নতুন দরজা। আর pip install দেওয়ার আগে এক মুহূর্ত ভাবুন: “এই নামটি কোথা থেকে পেলাম?”

পরীক্ষা ও ইন্টারভিউতে
  • Virtual environment কেন দরকার? প্রতিটি প্রজেক্টের package আলাদা রাখতে, যাতে এক প্রজেক্টের version অন্যটিকে না ভাঙে আর সিস্টেমের Python অক্ষত থাকে।
  • pip freeze আর pip list-এর পার্থক্য? দুটিই ইনস্টল করা package দেখায়; freeze লেখে name==version আকারে, যা সরাসরি requirements.txt-এ রাখা যায়।
  • ~=1.4.2 মানে কী? অন্তত 1.4.2, কিন্তু 1.5-এর নিচে।
  • wheel আর sdist-এর পার্থক্য? wheel আগে থেকে build করা, শুধু কপি করে ইনস্টল হয়; sdist সোর্স, ইনস্টলের সময় build করতে হয়।
  • venv activate করলে কী বদলায়? শুধু ওই terminal-এর PATH (শুরুতে venv-এর bin/Scripts), VIRTUAL_ENV আর প্রম্পট।

৯ · AI-এর যুগে — AI-এর ইনস্টল-পরামর্শ যাচাই

“ModuleNotFoundError এলে কী করব?” — AI-কে জিজ্ঞেস করলে প্রায়ই সঙ্গে সঙ্গে একটি ইনস্টল-কমান্ড পাবেন। অনেক সময় সেটি ঠিকই থাকে। কিন্তু AI মডেল ইন্টারনেটের বহু বছরের লেখা থেকে শেখে, যার বড় অংশ venv আর PEP 668-এর আগের যুগের। আর package-এর নামের ক্ষেত্রে AI-এর ভুল শুধু বিরক্তিকর নয়, বিপজ্জনক। এই ভুলগুলো চিনে রাখুন:

AI যা বলতে পারেসমস্যাসঠিক পথ
sudo pip install ...সিস্টেমের Python বদলায়, OS-এর টুল ভাঙতে পারে; root হিসেবে অচেনা কোড চালায়venv-এর ভেতরে python -m pip install
pip install --break-system-packages ...PEP 668-এর সুরক্ষা জোর করে ভাঙাvenv, pipx বা OS-এর package
venv-এর কথাই না বলে সরাসরি ইনস্টলসব প্রজেক্ট এক তাকে — নাদিয়ার সমস্যাপ্রথমে python -m venv .venv
শর্ত ছাড়া requirements.txtআজ চলে, ছয় মাস পরে অজানা কারণে ভাঙেহুবহু pin, বা lock file
এমন package-নাম, যা PyPI-তে নেই বা প্রায় অচেনাSlopsquatting-এর ঝুঁকিpypi.org-এ যাচাই, তারপর ইনস্টল
python setup.py installবহু আগে বাতিলের পথেpython -m pip install . (pyproject.toml দিয়ে)
import-এর নাম দিয়ে ইনস্টল (pip install cv2, pip install sklearn)distribution-এর নাম আলাদা; ভুল নামে অন্য বা ক্ষতিকর package আসতে পারেopencv-python, scikit-learn — ডকুমেন্টেশন থেকে নাম নিন

AI-কে ইনস্টল-সংক্রান্ত প্রশ্ন করার সময় নিজের পরিবেশ জানিয়ে দিন, আর নিরাপদ নিয়মগুলো শুরুতেই বেঁধে দিন:

আমি Windows 11-এ Python 3.13 দিয়ে একটি প্রজেক্ট করছি। প্রজেক্ট-ফোল্ডারে .venv নামে virtual environment আছে, VS Code-এ সেটিই interpreter হিসেবে বাছাই করা। কোড চালালে এই error আসছে: [পুরো error এখানে পেস্ট করো] নিয়মগুলো মেনে উত্তর দাও: ১. কোন package লাগবে বলো, আর PyPI-তে তার হুবহু distribution-নাম দাও (import-এর নাম নয়)। নামটি সম্পর্কে নিশ্চিত না হলে সেটা স্পষ্ট বলো। ২. সেই package-এর অফিসিয়াল ডকুমেন্টেশন বা source repository-র ঠিকানা দাও, যাতে আমি নিজে মিলিয়ে নিতে পারি। ৩. কমান্ড দাও শুধু venv-এর ভেতরে python -m pip দিয়ে, সাথে কোন version pin করা উচিত তার পরামর্শ। ৪. sudo, --break-system-packages, --user বা সিস্টেমের Python-এ ইনস্টল বলবে না। ৫. requirements.txt বা pyproject.toml-এ কী লাইন যোগ করব, সেটাও দেখাও।

AI-এর দেওয়া কোনো ইনস্টল-কমান্ড চালানোর আগে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে টুলের “কীভাবে” থেকে নকশার “কেন”-তে নিয়ে যাবে।

প্র ০১easy_install থেকে pip, তারপর wheel — Python-এ package ইনস্টলের পদ্ধতি এতবার বদলাল কেন?

শুরুর দিকে Python-এ “package ইনস্টল” মানে ছিল হাতে কাজ: আর্কাইভ নামিয়ে খুলে python setup.py install চালানো। এর ভিত ছিল standard library-র distutils. Dependency-র কোনো হিসাব ছিল না — একটি package-এর আর কী লাগে, তা পড়ে নিজেকেই খুঁজে আনতে হতো।

easy_install (২০০৪): Phillip J. Eby-র setuptools-এর সাথে আসে প্রথম স্বয়ংক্রিয় ইনস্টলার, যা নাম দিলে package খুঁজে, dependency-সহ নামাত। এর সাথে আসে “egg” ফরম্যাট। বড় অগ্রগতি, কিন্তু সমস্যাও ছিল: uninstall করার কোনো কমান্ড ছিল না, কী ইনস্টল হয়েছে তার পরিষ্কার তালিকা ছিল না, আর আধা-ইনস্টল অবস্থায় থেমে গেলে পরিবেশ এলোমেলো হয়ে থাকত।

pip (২০০৮): Ian Bicking শুরুতে “pyinstall” নামে, পরে pip নামে নতুন ইনস্টলার বানান। লক্ষ্য ছিল সরল আর পূর্বানুমেয় আচরণ: ইনস্টলের আগে সব নামানো, ইনস্টল করা ফাইলের রেকর্ড রাখা, uninstall, আর requirements ফাইল থেকে একসাথে ইনস্টল। pip freeze আর -r requirements.txt-এর প্রথা সেখান থেকেই। পরে pip-এর দায়িত্ব নেয় Python Packaging Authority (PyPA), আর Python 3.4 থেকে (PEP 453, ensurepip) pip Python-এর সাথেই আসে।

wheel (PEP 427, প্রস্তাব ২০১২): sdist থেকে ইনস্টল মানে প্রতিটি কম্পিউটারে build — C-কোড থাকলে compiler লাগবে। Windows ব্যবহারকারীর জন্য NumPy-র মতো package ইনস্টল প্রায় দুঃস্বপ্ন ছিল। wheel একটি সরল zip-ফরম্যাট, যা আগে থেকে build করা থাকে; ইনস্টল মানে কপি, আর কোনো কোড চালানোর দরকার নেই। পরে “manylinux” মানক (PEP 513, ২০১৬) Linux-এর জন্যও একটি wheel-এ বহু distribution চালানো সম্ভব করে।

প্রতিটি ধাপ আগের ধাপের একটি নির্দিষ্ট দুর্বলতা সারিয়েছে: হাতের কাজ → স্বয়ংক্রিয়, অস্বচ্ছ → রেকর্ডসহ, build-নির্ভর → তৈরি ফাইল। পুরোনো পথ একদিনে মোছা যায় না, কারণ হাজার পুরোনো প্রজেক্ট সেগুলোর ওপর দাঁড়িয়ে — তাই easy_install বা setup.py install এখনো পুরোনো টিউটোরিয়াল আর AI-এর উত্তরে দেখা যায়।

মূল উপলব্ধি: packaging-এর ইতিহাস হলো ধীরে ধীরে “জাদুকরী স্ক্রিপ্ট” থেকে “স্পষ্ট, যাচাইযোগ্য ফাইল ও মানক”-এর দিকে যাত্রা। কোনো পরামর্শে পুরোনো টুলের নাম দেখলে বুঝবেন সেটি কোন যুগের লেখা।

প্র ০২virtualenv তো ২০০৭ সাল থেকেই ছিল — তাহলে Python 3.3-এ আলাদা করে venv standard library-তে আনা হলো কেন?

virtualenv (২০০৭): এটিও Ian Bicking-এর কাজ — একই বছরগুলোতে তিনি pip-ও বানাচ্ছিলেন। সমস্যা ছিল পরিষ্কার: একই সার্ভারে কয়েকটি ওয়েব অ্যাপ চালাতে গেলে প্রতিটির আলাদা version লাগত, অথচ Python-এর site-packages একটিই। virtualenv একটি ফোল্ডারে Python-এর একটি “ছোট সংস্করণ” তৈরি করত, যার নিজস্ব site-packages থাকত।

কিন্তু Python-এর ভেতরে এমন কোনো ব্যবস্থা ছিল না, তাই virtualenv-কে নানা কৌশল করতে হতো: standard library-র কিছু ফাইল কপি বা symlink করা, Python-এর শুরুর site.py নিজের সংস্করণ দিয়ে বদলানো। প্রতিটি নতুন Python version-এ ভেতরের কোনো বদলে virtualenv ভাঙতে পারত, আর সেটি ঠিক করতে হতো আলাদা দলকে।

venv (PEP 405, Python 3.3, ২০১২): Carl Meyer-এর লেখা এই PEP সমস্যাটির গোড়া ধরে — virtual environment-এর ধারণাকে interpreter-এর নিজের অংশ বানায়। কৌশল খুব সরল: venv ফোল্ডারে একটি pyvenv.cfg ফাইল থাকে। Python শুরু হওয়ার সময় নিজের executable-এর পাশে বা এক ধাপ ওপরে এই ফাইল খোঁজে। পেলে সে বোঝে “আমি একটি venv-এর অংশ”: sys.prefix হয় venv-এর ফোল্ডার, sys.base_prefix থাকে আসল ইনস্টলে, standard library আসে আসল জায়গা থেকে, আর বাইরের package আসে venv-এর site-packages থেকে। কিছু কপি করার, কিছু বদলানোর দরকার নেই।

এতে লাভ: প্রতিটি Python-এর সাথেই venv আসে, বাড়তি কিছু ইনস্টল লাগে না; interpreter-এর বদলের সাথে venv-ও একসাথে বদলায়, তাই ভাঙে না। Python 3.4 থেকে venv তৈরি হলে তার ভেতরে pip-ও বসে যায়।

virtualenv হারিয়ে যায়নি — এটি এখনো রক্ষণাবেক্ষণ করা হয়, দ্রুততর ও কিছু বাড়তি সুবিধা দেয়, আর ভেতরে এখন venv-এর ব্যবস্থাই ব্যবহার করে। uv-এর মতো নতুন টুলও একই pyvenv.cfg-ভিত্তিক venv বানায়, তাই সবাই একই ভাষায় কথা বলে।

মূল উপলব্ধি: বাইরের টুল কোনো ধারণার প্রয়োজনীয়তা প্রমাণ করে; প্রমাণ হলে ভাষা নিজে সেটি সরল ও স্থায়ীভাবে নিজের ভেতরে নিয়ে নেয়। venv সেই পরিণতির উদাহরণ।

প্র ০৩setup.py তো দিব্যি কাজ করত — তাহলে pyproject.toml-এর মতো একটি “স্থির” ফাইলে যাওয়ার দরকার পড়ল কেন?

setup.py একটি সাধারণ Python স্ক্রিপ্ট, যা চালালে প্রজেক্টের তথ্য বেরোয়। শুনতে নমনীয়, আর ঠিক সেটিই সমস্যা। প্রজেক্টের নাম আর dependency জানতে হলেও স্ক্রিপ্টটি চালাতে হতো। সেই স্ক্রিপ্ট যা খুশি করতে পারত — অন্য package import করা, ফাইল পড়া, এমনকি ইন্টারনেটে যাওয়া। একটি package-এর তথ্য জানতে অচেনা কোড চালানো নিরাপত্তার দিক থেকেও দুর্বল।

সবচেয়ে বড় জট ছিল “ডিম আগে না মুরগি” সমস্যা। ধরুন setup.py চলতে Cython বা setuptools-এর নতুন version লাগে। কিন্তু এটি জানা যাবে কীভাবে? setup.py চালালে — আর চালাতে গেলেই তো সেটি লাগবে! কোনো মানক জায়গা ছিল না, যেখানে লেখা থাকবে “আমাকে build করতে আগে এগুলো লাগবে”।

PEP 518 (২০১৬): Brett Cannon, Nathaniel Smith ও Donald Stufft-এর এই প্রস্তাব pyproject.toml ফাইল আর তার [build-system] অংশ আনে — build করার আগে কী কী লাগবে, একটি স্থির, পড়া যায় এমন ফাইলে। ফরম্যাট হিসেবে TOML বেছে নেওয়া হয়, কারণ JSON-এ comment লেখা যায় না, YAML-এর নিয়ম জটিল ও অপ্রত্যাশিত, আর INI-র কোনো স্পষ্ট মানক ছিল না। Python 3.11 থেকে standard library-তে tomllib দিয়ে TOML পড়াও যায়।

PEP 517: pip আর build টুলের মধ্যে একটি মানক যোগাযোগ-পদ্ধতি দেয়। এর ফলে setuptools-এর একচেটিয়া অবস্থান ভাঙে — flit, hatchling, poetry-core, maturin (Rust-এর জন্য) সবাই build backend হতে পারে। PEP 621 (২০২০) প্রজেক্টের নাম, version, dependency রাখার সর্বজনীন [project] অংশ মানক করে, আর PEP 660 (২০২১) এই নতুন ব্যবস্থায় editable install (pip install -e .) সম্ভব করে।

ফল: একটি ফাইল খুলেই মানুষ ও টুল দুজনেই প্রজেক্টের পরিচয় পড়তে পারে, কোনো কোড না চালিয়ে। setup.py এখনো জটিল build-এর জন্য রাখা যায়, তবে নতুন প্রজেক্টে সাধারণত শুধু pyproject.toml-ই যথেষ্ট।

মূল উপলব্ধি: “কোড হিসেবে কনফিগ” নমনীয়, কিন্তু অস্বচ্ছ ও ঝুঁকিপূর্ণ; “ঘোষণা হিসেবে কনফিগ” সীমিত, কিন্তু পড়া, যাচাই ও টুল দিয়ে প্রক্রিয়া করা সহজ। বড় ইকোসিস্টেম দ্বিতীয়টির দিকেই যায়।

প্র ০৪Linux distribution-গুলো pip-কে নিজের Python-এ ইনস্টল করতে বাধা দেয় (PEP 668) — এটা কি ব্যবহারকারীর স্বাধীনতায় হস্তক্ষেপ, নাকি দরকারি সুরক্ষা?

সমস্যাটি বুঝতে Linux-এর গঠন দেখতে হবে। Debian, Ubuntu বা Fedora-র মতো distribution-এ প্যাকেজ-ম্যানেজারের (apt, dnf) নিজস্ব Python package আছে — যেমন python3-requests. OS-এর অনেক টুল Python-এ লেখা, আর সেগুলো এই নির্দিষ্ট, পরীক্ষিত version-গুলোর ওপর নির্ভর করে। Ubuntu-র কিছু সিস্টেম-টুল এর উদাহরণ।

এখন কেউ sudo pip install --upgrade requests দিলে pip একই site-packages-এ নতুন version বসায়, apt-এর অজান্তে। এতে দুটি দুর্ঘটনা ঘটতে পারে: OS-এর কোনো টুল নতুন version-এ ভেঙে যায়, অথবা পরে apt আপডেট করতে গিয়ে pip-এর বসানো ফাইল উল্টে দেয়। এমন ভাঙা সিস্টেম নিয়ে বছরের পর বছর বাগ-রিপোর্ট জমেছে, আর সমস্যার উৎস খোঁজা ছিল কঠিন — কে কোন ফাইল বসিয়েছে?

PEP 668 (২০২১ সালে প্রস্তাবিত, ২০২২ সালে গৃহীত): সমাধানটি সহজ। distribution তার Python-এর ফোল্ডারে EXTERNALLY-MANAGED নামে একটি চিহ্ন-ফাইল রাখে। pip (23.0 থেকে) সেটি দেখলে সিস্টেমে ইনস্টল করতে অস্বীকার করে, আর বার্তায় বিকল্প বলে দেয় — venv, pipx বা OS-এর package. venv-এর ভেতরে এই চিহ্ন থাকে না, তাই সেখানে সব স্বাভাবিক। Debian 12 (২০২৩), Ubuntu 23.04 আর Homebrew-এর Python এই নীতি গ্রহণ করে।

স্বাধীনতা কি গেল? না। দরজা বন্ধ নয়, তালায় একটি সতর্কবার্তা ঝোলানো। --break-system-packages দিয়ে জোর করে ইনস্টল এখনো করা যায় — নামটিই পরিণতি জানিয়ে দেয়। আর venv বানাতে কোনো অনুমতি লাগে না। এটি “নিরাপদ পথকে সহজ, ঝুঁকিপূর্ণ পথকে সচেতন সিদ্ধান্ত” বানানোর নকশা — যাকে বলা হয় ভালো ডিফল্ট।

অনেক ব্যবহারকারী প্রথমে বিরক্ত হয়েছেন, কারণ অভ্যস্ত কমান্ড হঠাৎ কাজ করছিল না, আর অনেক পুরোনো টিউটোরিয়াল ভুল হয়ে গেল। কিন্তু দীর্ঘমেয়াদে এটি নতুনদের সেই অভ্যাসের দিকেই ঠেলে দেয়, যা অভিজ্ঞরা আগে থেকেই মানতেন: প্রতিটি প্রজেক্টে venv.

মূল উপলব্ধি: যে জিনিসের মালিক অন্য কেউ (এখানে OS), তাতে না জানিয়ে বদল করলে দুই মালিকের সংঘাত হয়। PEP 668 মালিকানার সীমারেখা স্পষ্ট করেছে — সিস্টেমের Python OS-এর, আপনার প্রজেক্টের Python আপনার venv-এ।

প্র ০৫PyPI-তে ক্ষতিকর package-এর আক্রমণ কেমন হয়েছে, আর PyPI কেন 2FA ও trusted publishing চালু করল?

খোলা ভাণ্ডারের শক্তিই তার দুর্বলতা: যে কেউ আপলোড করতে পারে, আর লাখো মানুষ pip install দিয়ে বিশ্বাস করে নামায়। কয়েকটি ঘটনা সমস্যার রূপগুলো দেখায়।

  • Typosquatting (২০১৭): স্লোভাকিয়ার জাতীয় নিরাপত্তা দপ্তর (NBU) PyPI-তে জনপ্রিয় package-এর নামের সামান্য ভিন্ন বানানে কয়েকটি ক্ষতিকর package খুঁজে পায়, যেগুলো ইনস্টলের সময় কম্পিউটারের তথ্য পাঠাত। এরপর থেকে নিরাপত্তা গবেষকেরা নিয়মিতই এমন package ধরছেন, আর PyPI সেগুলো সরাচ্ছে।
  • অ্যাকাউন্ট দখল (২০২২): ctx নামের একটি পুরোনো package-এর মালিকের ইমেইলের ডোমেইনের মেয়াদ শেষ হয়ে গিয়েছিল। আক্রমণকারী ডোমেইনটি কিনে পাসওয়ার্ড রিসেট করে অ্যাকাউন্ট দখল করে, তারপর নতুন version আপলোড করে, যা environment variable থেকে গোপন তথ্য চুরি করত। কোডে নয়, দুর্বলতা ছিল অ্যাকাউন্টের নিরাপত্তায়।
  • Dependency confusion (২০২২): PyTorch-এর nightly build একটি ভেতরের dependency-র নাম (torchtriton) ব্যবহার করত, যা পাবলিক PyPI-তে কেউ আগে থেকে নেয়নি। আক্রমণকারী সেই নামে ক্ষতিকর package আপলোড করে, আর কয়েক দিনের জন্য nightly ইনস্টল করা ব্যবহারকারীরা সেটি পেয়ে যান।

PyPI-র প্রতিক্রিয়া: অ্যাকাউন্ট-দখলই সবচেয়ে বিপজ্জনক, কারণ তখন আসল নামের package-ই ক্ষতিকর হয়ে যায় — বানান যাচাই কোনো কাজে আসে না। তাই ২০২২ সালে সবচেয়ে গুরুত্বপূর্ণ প্রজেক্টের মালিকদের জন্য two-factor authentication (2FA) বাধ্যতামূলক করা হয়, আর ২০২৪ সালের শুরু থেকে সব আপলোডকারী অ্যাকাউন্টের জন্য। ২০২৩ সালে আসে trusted publishing: দীর্ঘস্থায়ী token কোথাও জমা না রেখে GitHub Actions-এর মতো CI থেকে স্বল্পমেয়াদি পরিচয়-প্রমাণ দিয়ে প্রকাশ করা যায় — চুরি করার মতো স্থায়ী চাবিই থাকে না। পাশাপাশি ক্ষতিকর package রিপোর্ট ও দ্রুত সরানোর ব্যবস্থা উন্নত করা হয়েছে।

নতুন রূপ — slopsquatting: AI-এর বানানো package-নাম নিয়ে গবেষণায় দেখা গেছে, মডেলগুলো উল্লেখযোগ্য হারে অস্তিত্বহীন নাম বলে, আর একই বানানো নাম বারবার ফিরে আসে। এই প্রবণতার জন্যই “slopsquatting” নামটি চালু হয়েছে। ভাণ্ডারের সুরক্ষা যত বাড়ুক, শেষ সিদ্ধান্তটি — কোন নাম ইনস্টল করবেন — আপনার হাতেই।

মূল উপলব্ধি: supply chain-এর নিরাপত্তা একটি স্তরে হয় না: ভাণ্ডার (2FA, trusted publishing), টুল (hash, lock file, pip-audit) আর আপনি (নাম যাচাই, কম dependency, pin) — তিনটি স্তর মিলেই সুরক্ষা।

অনুশীলন

বেশিরভাগ কাজ নিজের কম্পিউটারের terminal-এ করার। কম্পিউটার না থাকলে উত্তর পড়ে প্রতিটি ধাপের কারণ বুঝে নিন।

  1. দুই তাক, দুই version: দুটি খালি ফোল্ডার বানান — old-app আর new-app. প্রতিটিতে venv তৈরি করে old-app-এ rich==12.6.0 আর new-app-এ শর্ত ছাড়া rich ইনস্টল করুন। তারপর প্রমাণ করুন যে দুটি venv-এ দুটি আলাদা version আছে, আর সিস্টেমের Python-এ rich নেই।
    cd old-app
    python -m venv .venv
    .venv\Scripts\Activate.ps1           # Mac/Linux: source .venv/bin/activate
    python -m pip install "rich==12.6.0"
    python -m pip show rich              # Version: 12.6.0, Location: ...old-app\.venv\...
    deactivate
    cd ..\new-app                        # একই কাজ, শুধু: python -m pip install rich
    python -m pip show rich              # নতুন version, Location: ...new-app\.venv\...
    deactivate
    python -m pip show rich              # সিস্টেমে: "WARNING: Package(s) not found: rich"

    প্রমাণ হলো pip show-এর Version আর Location লাইন — দুটি ভিন্ন ফোল্ডার, দুটি ভিন্ন version. শেষ লাইনে venv বন্ধ অবস্থায় সিস্টেমের Python rich খুঁজে পায় না, অর্থাৎ সিস্টেম অক্ষত। PEP 668-যুক্ত Linux-এ শেষ কমান্ডটি শুধু দেখায়, কিছু ইনস্টল করে না, তাই নিরাপদ।

  2. ভুল ধরুন: একজন সহকর্মী এই requirements.txt পাঠিয়েছেন। কোন কোন লাইনে সমস্যা, আর কেন? reqeusts · pandas · numpy==1.26.4 · sklearn · django>=4.2 · numpy==2.1.0
    • reqeusts — বানান ভুল (e আর u বদল)। এটি typosquatting-এর ঠিক সেই ফাঁদ; আসলটি requests.
    • pandas — কোনো version-শর্ত নেই; ছয় মাস পরে ভিন্ন version আসবে।
    • sklearn — import-এর নাম, distribution-এর নয়। আসল নাম scikit-learn; পুরোনো sklearn নামের package এখন ইচ্ছাকৃতভাবে ইনস্টলে error দেয়, যাতে মানুষ ভুল নামটি ব্যবহার না করে।
    • django>=4.2 — ঢিলে শর্ত; 5.x, 6.x সবই চলে আসতে পারে, যা পুরোনো কোড ভাঙতে পারে। নাদিয়ার প্রজেক্টের জন্য django~=4.2 বা হুবহু pin ভালো।
    • numpy দুবার, দুটি ভিন্ন হুবহু version — দুটি একসাথে পূরণ অসম্ভব; pip error দেবে। একটি রাখতে হবে।

    শুধু numpy==1.26.4 লাইনটি একা ঠিক ছিল, যদি দ্বিতীয় numpy না থাকত।

  3. pyproject.toml লিখুন: “cricket-score” নামে একটি ছোট CLI প্রজেক্টের জন্য pyproject.toml লিখুন। শর্ত: version 0.1.0, Python 3.11 বা নতুন, dependency হিসেবে requests (অন্তত 2.31) আর rich (13-এর সামঞ্জস্যপূর্ণ version), আর terminal-এ score লিখলে cricket_score/cli.py-এর main() চলবে। তারপর বলুন, তৃষা প্রজেক্টটি পেয়ে কোন দুটি কমান্ডে চালানোর উপযোগী করবেন।
    [build-system]
    requires = ["hatchling"]
    build-backend = "hatchling.build"
    
    [project]
    name = "cricket-score"
    version = "0.1.0"
    requires-python = ">=3.11"
    dependencies = ["requests>=2.31", "rich~=13.0"]
    
    [project.scripts]
    score = "cricket_score.cli:main"

    rich~=13.0 মানে অন্তত 13.0, কিন্তু 14-এর নিচে। distribution-এর নামে হাইফেন (cricket-score), কিন্তু import-এর package-এ আন্ডারস্কোর (cricket_score), কারণ Python-এর নামে হাইফেন চলে না। তৃষা venv বানিয়ে activate করার পর: python -m pip install -e . (প্রজেক্ট ও dependency ইনস্টল), তারপর score.

  4. রহস্য সমাধান: সাকিব terminal-এ venv activate করে pip install pandas দিলেন, সফল বার্তাও এলো। কিন্তু VS Code-এর ▷ বোতাম চাপতেই ModuleNotFoundError: No module named 'pandas'. তিনটি সম্ভাব্য কারণ আর প্রতিটি যাচাইয়ের উপায় বলুন।
    • VS Code অন্য interpreter ব্যবহার করছে। status bar-এ দেখুন ('.venv') আছে কি না; না থাকলে “Python: Select Interpreter” → .venv. এটিই সবচেয়ে সাধারণ কারণ।
    • pandas অন্য venv বা সিস্টেমে গেছে। যে terminal-এ ইনস্টল করেছেন, সেখানে python -m pip show pandas-এর Location দেখুন — সেটি এই প্রজেক্টের .venv কি না। হয়তো অন্য ফোল্ডারের venv চালু ছিল।
    • শুধু pip লেখায় ভুল pip চলেছে। PATH-এ অন্য কোনো pip আগে থাকলে এমন হয়। এরপর থেকে python -m pip.

    এক লাইনে নিশ্চিত হওয়ার উপায়: VS Code-এ চলা প্রোগ্রামে import sys; print(sys.executable) যোগ করে দেখুন কোন python চলছে, আর terminal-এ python -c "import sys; print(sys.executable)" — দুটি একই পথ হতে হবে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমসহকর্মীর git-এ রাখা একটি Python প্রজেক্ট (requirements.txt-সহ) নিজের কম্পিউটারে চালানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. git clone করে প্রজেক্ট-ফোল্ডারে ঢোকা
  2. python -m venv .venv দিয়ে নতুন virtual environment তৈরি
  3. venv activate করা (প্রম্পটে (.venv) দেখা)
  4. python -m pip install -r requirements.txt
  5. প্রজেক্টের মূল স্ক্রিপ্ট চালানো

Python-এর requirements ফাইলে django~=4.2 লেখা থাকলে নিচের কোন version গ্রহণযোগ্য?

  • 4.1.13
  • 5.0.0
  • 4.2.11
  • শুধু 4.2.0, অন্য কিছু নয়

একটি Python প্রোগ্রামের ভেতরে sys.prefix != sys.base_prefix মানটি True এলে কী বোঝায়?

  • Python-এর version পুরোনো
  • প্রোগ্রামটি একটি virtual environment-এর ভেতরে চলছে
  • pip ইনস্টল করা নেই
  • প্রোগ্রামটি admin অধিকারে চলছে

একটি Python প্রজেক্ট git-এ রাখার সময় নিচের কোনটি repository-তে রাখা উচিত নয়?

  • pyproject.toml
  • requirements.txt
  • uv.lock বা অন্য lock file
  • .venv/ ফোল্ডার

নতুন Ubuntu-তে venv ছাড়া pip install requests দিলে externally-managed-environment error আসে। একটি প্রজেক্টের জন্য সবচেয়ে উপযুক্ত সমাধান কোনটি?

  • প্রজেক্ট-ফোল্ডারে python3 -m venv .venv বানিয়ে activate করে ইনস্টল করা
  • sudo pip install requests
  • pip install --break-system-packages requests
  • সিস্টেমের Python মুছে নতুন করে ইনস্টল করা

Python প্রজেক্টের pyproject.toml ফাইলের [project] অংশে সাধারণত কী থাকে?

  • virtual environment-এর ফোল্ডারের পথ
  • PyPI-র পাসওয়ার্ড ও API key
  • প্রজেক্টের নাম, version, প্রয়োজনীয় Python version আর dependency-তালিকা
  • pip-এর ইনস্টল করা প্রতিটি ফাইলের তালিকা

একটি AI সহকারী এমন একটি Python package ইনস্টল করতে বলল, যার নাম আপনি কখনো শোনেননি। সবচেয়ে নিরাপদ প্রথম পদক্ষেপ কোনটি?

  • venv-এর ভেতরে ইনস্টল করে দেখা — venv তো সব ঝুঁকি আটকায়
  • pypi.org-এ নামটি খুঁজে package-এর অস্তিত্ব, বয়স, source repository আর লেখক যাচাই করা
  • sudo দিয়ে ইনস্টল করা, যাতে কোনো permission error না আসে
  • নাম না পেলে কাছাকাছি বানানের কোনো package ইনস্টল করা

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. এক প্রজেক্ট = এক venv: python -m venv .venv, তারপর activate. venv শুধু একটি ফোল্ডার — ভাঙলে মুছে আবার বানান; git-এ কখনো নয়।
  2. Activate মানে PATH-এর শুরুতে venv-এর bin/Scripts — তাই python আর pip venv-এর। sys.prefix != sys.base_prefix হলে আপনি venv-এ।
  3. সবসময় python -m pip: install, list, show, freeze. pip PyPI থেকে wheel এনে চালু venv-এর site-packages-এ বসায়, dependency-সহ।
  4. Version লিখে রাখুন: == হুবহু, ~= সামঞ্জস্যপূর্ণ, >= ন্যূনতম। pyproject.toml বলে “কী লাগে”, lock file বলে “ঠিক কী ইনস্টল হবে”।
  5. ইনস্টলের আগে নাম যাচাই: typosquatting আর AI-এর বানানো নাম বাস্তব বিপদ। sudo pip আর --break-system-packages নয়।

পরের পর্বে: প্রজেক্ট সাজানো হলো, dependency-ও ঠিকঠাক। কিন্তু ফাইল না পেলে, ইন্টারনেট না থাকলে, ব্যবহারকারী ভুল ইনপুট দিলে প্রোগ্রাম কী করবে? ModuleNotFoundError-এর মতো লাল লেখাগুলো আসলে কী, আর try/except দিয়ে কীভাবে সেগুলো সামলাতে হয় — exception-এর পর্ব।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান