যদি কোনো প্যাকেজের ভেতরের একটি পাইথন ফাইল সরাসরি চালানোর সময় ImportError: attempted relative import with no known parent package দেখায়, তবে প্যাকেজের উপরের ডিরেক্টরি থেকে সেটিকে একটি মডিউল হিসেবে চালু করুন। macOS-এ, Terminal-এ প্রোজেক্টের রুট ডিরেক্টরিতে যান, তারপর python3 -m package.module চালান। এটি পাইথনকে সেই প্যাকেজ কনটেক্সট প্রদান করে যা সরাসরি ফাইল পাথ দেয় না।
সরাসরি একটি ফাইল চালালে রিলেটিভ ইমপোর্ট ব্যর্থ হয় কেন?
from .helpers import format_result-এর মতো একটি রিলেটিভ ইমপোর্ট মডিউলের প্যাকেজ নামের ওপর নির্ভর করে। পাইথন বর্তমান প্যাকেজ এবং এর প্যারেন্ট প্যাকেজগুলো শনাক্ত করতে শুরুর ডট ব্যবহার করে। আপনি যখন python3 path/to/file.py দিয়ে একটি ফাইল চালান, তখন পাইথন সেই ফাইলটিকে শীর্ষ স্তরের __main__ মডিউল হিসেবে চালায়; এটি ডট রিসলভ করার জন্য প্রয়োজনীয় প্যাকেজ মেটাডেটা প্রদান করে না। ফলে ফাইলটির পাশেই helpers.py থাকা সত্ত্বেও ইমপোর্টটি ব্যর্থ হতে পারে। Python-এর import reference-এ রিলেটিভ ইমপোর্ট এবং __main__-এর বিশেষ ব্যবহারের বিশদ বর্ণনা রয়েছে।
উদাহরণস্বরূপ, ধরা যাক আপনার প্রোজেক্টটি এভাবে সাজানো হয়েছে:
project/
└── myapp/
├── __init__.py
├── cli.py
└── helpers.py
যদি cli.py-এর মধ্যে from .helpers import format_result থাকে, তবে project থেকে python3 myapp/cli.py চালানো একটি ভুল ইনভোকেশন। সমস্যাটি হলো পাইথনকে যেভাবে ফাইলটি চালাতে বলা হয়েছিল সেখানে, রিলেটিভ ইমপোর্ট বা ফাইলের অবস্থান ভুল এমনটি নাও হতে পারে।
macOS-এ প্রোজেক্টের রুট ডিরেক্টরি থেকে মডিউলটি চালান
Terminal খুলুন এবং myapp ধারণকারী ডিরেক্টরিতে যান। উদাহরণ পাথটিকে আপনার প্রোজেক্টের আসল অবস্থান দিয়ে প্রতিস্থাপন করুন। পাথটিকে কোটেশনের মধ্যে রাখুন যাতে শেল স্পেসগুলো সঠিকভাবে পরিচালনা করতে পারে; macOS Terminal-এর ডিফল্ট শেল হলো zsh। Apple-এর Terminal নির্দেশিকা-তে সেই ডিফল্ট শেলের বিবরণ রয়েছে।
cd "/Users/yourname/Projects/project"
python3 -m myapp.cli
-m অপশনটি পাইথনকে ডটযুক্ত প্যাকেজ নাম দ্বারা মডিউলটি খুঁজে বের করে চালানোর নির্দেশ দেয়। কমান্ডে প্যাকেজের নাম রাখুন এবং .py ও ফাইলসিস্টেম স্ল্যাশগুলো বাদ দিন। -m দিয়ে ইনভোক করা হলে পাইথনের মডিউল সার্চ পাথ বর্তমান ডিরেক্টরি থেকে শুরু হয়, আর এই কারণেই প্রোজেক্টের রুট থেকে চালানো গুরুত্বপূর্ণ। Python-এর sys.path ডকুমেন্টেশন-এ এই প্রারম্ভিক পাথের ব্যাখ্যা দেওয়া আছে।
কমান্ডটি যদি ভুল পাইথন ইনস্টলেশন ব্যবহার করে, তবে প্রথমে প্রোজেক্টের বিদ্যমান ভার্চুয়াল এনভায়রনমেন্ট সক্রিয় করুন, তারপর একই python -m প্যাটার্ন ব্যবহার করুন। প্রোজেক্ট রুটে একটি সাধারণ .venv-এর জন্য:
cd "/Users/yourname/Projects/project"
source .venv/bin/activate
python -m myapp.cli
অ্যাক্টিভেশনের পর python ব্যবহার করলে এনভায়রনমেন্টের ইন্টারপ্রেটার নির্বাচিত হয়। আপনার এনভায়রনমেন্টের নাম বা অবস্থান ভিন্ন হলে, অ্যাক্টিভেশন পাথটি মানিয়ে নিন; .venv বিদ্যমান আছে বলে ধরে নেবেন না। আপনি python -c 'import sys; print(sys.executable)' দিয়ে নির্বাচিত ইন্টারপ্রেটার যাচাই করতে পারেন।
আপনি যদি প্যাকেজটি নিজেই চালাতে চান
যে কমান্ডটি পুরো প্যাকেজ চালু করার উদ্দেশ্যে তৈরি, তার জন্য প্যাকেজের ভেতরে __main__.py যোগ করুন এবং এন্ট্রি পয়েন্টটি সংক্ষিপ্ত রাখুন:
myapp/
├── __init__.py
├── __main__.py
├── cli.py
└── helpers.py
myapp/__main__.py-এ প্যাকেজের এন্ট্রি ফাংশনটি ইমপোর্ট করুন এবং কল করুন। তারপর প্রোজেক্টের রুট থেকে python3 -m myapp চালান। -m ইনভোকেশনের এই রূপের জন্য পাইথন প্যাকেজের __main__.py এক্সিকিউট করে, এবং সেই ফাইলটি প্যাকেজ-রিলেটিভ ইমপোর্ট ব্যবহার করতে পারে। Python __main__ ডকুমেন্টেশন-এ প্যাকেজের এই এন্ট্রি-পয়েন্ট প্যাটার্নটি ব্যাখ্যা করা হয়েছে।
ত্রুটি বজায় থাকলে এই বিবরণগুলো পরীক্ষা করুন
- আপনার ওয়ার্কিং ডিরেক্টরি নিশ্চিত করুন। Terminal-এ
pwdচালান এবং নিশ্চিত করুন যে এটি প্রোজেক্ট রুট অর্থাৎmyappধারণকারী ডিরেক্টরিটি প্রদর্শন করছে। প্যাকেজটি যদি উদাহরণস্বরূপsrc-এর অধীনে নেস্টেড থাকে, তবে ইমপোর্টযোগ্য প্যাকেজ নাম ব্যবহার করুন এবং নিশ্চিত করুন যে প্রোজেক্টের কনফিগার করা এনভায়রনমেন্ট এটিকে উপলভ্য করে; কেবল ভুল প্যারেন্ট ফোল্ডারে পরিবর্তন করলে মডিউল ডিসকভারি ঠিক হবে না। - আসল প্যাকেজ নাম ব্যবহার করুন।
python3 -m myapp.cli-এmyappঅবশ্যই প্যাকেজ ডিরেক্টরির নাম (বা এর ইমপোর্ট নাম) হতে হবে, এবংcliহবে.pyছাড়া মডিউলের ফাইলের নাম। - ট্রেসব্যাক লাইনটি পরীক্ষা করুন। রিলেটিভ ইমপোর্টটি সমাধান হলেও যদি পরবর্তী কোনো ইমপোর্ট ব্যর্থ হয়, তবে সেটি ভিন্ন কোনো অনুপস্থিত মডিউল বা ডিপেন্ডেন্সি হতে পারে। লঞ্চ কনটেক্সট ঠিক করা কেবল প্যাকেজ-কনটেক্সটের ব্যর্থতার সমাধান করে।
- প্রথম সমাধান হিসেবে
PYTHONPATHএড়িয়ে চলুন। এটি পাইথনের ইমপোর্ট সার্চ পাথ পরিবর্তন করে, তবে এটি নিজে থেকে একটি সরাসরি এক্সিকিউট করা ফাইলকে সঠিকভাবে নামযুক্ত প্যাকেজ মডিউলে রূপান্তর করে না। পাইথন আরও সতর্ক করে যেPYTHONPATHএকাধিক পাইথন সংস্করণ এবং এনভায়রনমেন্টকে প্রভাবিত করতে পারে। Python-এর path initialization reference-এ এর পরিধি বর্ণনা করা হয়েছে। - সরাসরি ফাইল চালানো যদি বাধ্যতামূলক হয়, তবে কোড পরিবর্তন করে একটি উপযুক্ত অ্যাবসোলিউট ইমপোর্ট ব্যবহার করার কথা বিবেচনা করুন এবং নিশ্চিত করুন যে প্যাকেজটি ইনস্টল করা আছে বা অন্যথায় সেই এনভায়রনমেন্টে ইমপোর্টযোগ্য। এটি একটি ভিন্ন সেটআপের পছন্দ; প্যাকেজ-রিলেটিভ ইমপোর্টের সাথে ফাইলটি একটি স্বতন্ত্র স্ক্রিপ্টের মতো আচরণ করবে এমন প্রত্যাশা মেশাবেন না।
ব্যবহারিক নিয়মটি হলো ইমপোর্ট স্টাইলের সাথে কমান্ডের মিল রাখা: প্যাকেজ-রিলেটিভ ইমপোর্টের জন্য, প্যাকেজের প্যারেন্ট ডিরেক্টরি থেকে python -m দিয়ে মডিউলটি চালান। প্যাকেজ কমান্ডের জন্য, __main__.py সরবরাহ করুন এবং -m দিয়ে প্যাকেজটি চালান।
সূত্রসমূহ
- 5. The import system — Python Software Foundation
- The initialization of the sys.path module search path — Python Software Foundation
- main — Top-level code environment — Python Software Foundation
- Change the default shell in Terminal on Mac — Apple