إذا ظهر لك الخطأ IsADirectoryError: [Errno 21] Is a directory في Python، فهذا يعني أن الكود نفّذ عملية خاصة بملف لكنه مرّر إليها مسار مجلد. يحدث ذلك كثيرًا مع open() وPath.open() وPath.read_text() عندما يكون المسار مثل reports بدل ملف فعلي مثل reports/summary.txt.
IsADirectoryError: [Errno 21] Is a directory: 'reports'
الحل ليس تغيير الترميز أو إعادة تثبيت pathlib. افحص المسار نفسه: إذا كنت تريد قراءة ملف فتأكد باستخدام is_file()، وإذا كان المسار مجلدًا فاستخدم is_dir() ثم اختر الملف الموجود داخله. هذا المقال من سلسلة مشكلة وحل في بايثون يشرح الحالات العملية بدون تحويل الموضوع إلى درس عام عن المسارات.
{alertInfo} الفكرة ببساطة: وجود المسار عبرexists()لا يعني أنه ملف. قبل القراءة، تأكد أن المسار يشير إلى ملف، ثم تعامل معIsADirectoryErrorصراحةً إذا كانت حالة المسار قد تتغير.
{getToc} $title={محتوى المقال}
حل IsADirectoryError: [Errno 21] Is a directory في Python
يظهر الخطأ عندما تحاول استخدام مجلد كما لو كان ملفًا. في الأنظمة التي تعرض الصيغة [Errno 21] Is a directory، يشير الخطأ إلى حالة EISDIR: طلبت عملية ملف على مسار هو مجلد. وقد تختلف صياغة رسالة النظام بين البيئات، لكن معنى IsADirectoryError نفسه هو أن العملية المطلوبة لا تناسب مجلدًا.
أمثلة العمليات التي تتوقع ملفًا:
- قراءة النص عبر
open()أوread_text(). - الكتابة داخل ملف عبر
write_text()أوopen(..., "w"). - قراءة محتوى ملف CSV أو JSON من مسار يشير في الحقيقة إلى مجلد.
إذا كان المطلوب مراجعة أساسيات فتح الملفات وأوضاع القراءة والكتابة نفسها، راجع شرح التعامل مع الملفات في Python. أما هنا فالتركيز على تشخيص كون المسار مجلدًا بدل ملف.
المثال التالي يعيد إنتاج المشكلة:
from pathlib import Path
path = Path("reports")
with path.open("r", encoding="utf-8") as file:
content = file.read()
print(content)
إذا كان reports مجلدًا موجودًا، فإن path.open() لن يستطيع قراءته كنص، لأن هذه العملية تحتاج اسم ملف مثل reports/summary.txt.
في هذا التنظيم:
# خطأ: reports مجلد
Path("reports").read_text(encoding="utf-8")
# صحيح: summary.txt ملف داخل المجلد
file_path = Path("reports") / "summary.txt"
لماذا لا يكفي exists() لحل IsADirectoryError؟
قد يحاول بعض المبتدئين حل المشكلة بهذا الشكل:
from pathlib import Path
path = Path("reports")
if path.exists():
content = path.read_text(encoding="utf-8")
المشكلة أن exists() يجيب عن سؤال واحد فقط: هل يوجد هذا المسار؟ لكنه لا يخبرك هل هو ملف أم مجلد. لذلك قد تكون النتيجة True للمجلد reports، ثم يظهر الخطأ عند محاولة القراءة.
الحل هو فحص نوع المسار قبل محاولة قراءة محتواه:
from pathlib import Path
path = Path("reports")
if path.is_file():
content = path.read_text(encoding="utf-8")
print(content)
elif path.is_dir():
print("المسار يشير إلى مجلد، وليس ملفًا.")
else:
print("المسار غير موجود.")
الفرق بين exists وis_file وis_dir
| الدالة | السؤال الذي تجيب عنه | مثال لنتيجة محتملة |
|---|---|---|
exists() |
هل يوجد المسار أصلًا؟ | قد تكون True للملف أو المجلد. |
is_file() |
هل يشير المسار إلى ملف عادي؟ | True لملف مثل notes.txt. |
is_dir() |
هل يشير المسار إلى مجلد؟ | True لمجلد مثل reports. |
توفر مكتبة pathlib هذه الدوال للتعامل الواضح مع المسارات. راجع أيضًا درس شرح مكتبة pathlib للتعامل مع مسارات الملفات والمجلدات لتفهم طريقة بناء المسارات باستخدام العامل /.
حل IsADirectoryError عند معرفة اسم الملف داخل المجلد
عندما تعرف أن الملف المطلوب موجود داخل مجلد محدد، ابنِ مساره كاملًا بدل فتح اسم المجلد:
from pathlib import Path
reports_folder = Path("reports")
file_path = reports_folder / "summary.txt"
if file_path.is_file():
content = file_path.read_text(encoding="utf-8")
print(content)
else:
print("لم يتم العثور على الملف المطلوب.")
هذه الطريقة أفضل من كتابة مسار نصي طويل مثل "reports/summary.txt"، خصوصًا عندما تحتاج إلى بناء مسارات متعددة داخل مشروعك.
حل IsADirectoryError مع Path.read_text() وopen() باستخدام try/except
في البرامج الحقيقية، لا تعتمد على الفحص فقط. قد يتغير المسار بين لحظة الفحص ولحظة القراءة، أو تحدث مشكلة في الصلاحيات. لذلك استخدم فحصًا واضحًا للمستخدم، ثم تعامل مع الأخطاء المحتملة بشكل محدد. وإذا احتجت أساسيات بنية الاستثناءات نفسها، راجع شرح try وexcept في بايثون.
from pathlib import Path
def read_text_file(path_text):
path = Path(path_text)
if path.is_dir():
print(f"لا يمكن قراءة المجلد كملف: {path}")
return None
if not path.is_file():
print(f"الملف غير موجود: {path}")
return None
try:
return path.read_text(encoding="utf-8")
except PermissionError:
print("لا توجد صلاحية كافية لقراءة هذا الملف.")
except IsADirectoryError:
print("المسار أصبح مجلدًا أو يشير إلى مجلد بدل ملف.")
except UnicodeDecodeError:
print("تعذر قراءة الملف بترميز UTF-8.")
return None
content = read_text_file("reports/summary.txt")
if content is not None:
print(content)
لاحظ أننا لم نستخدم except Exception أو except: بلا تحديد. التقاط الأخطاء المحددة يجعل الرسالة أوضح ولا يخفي أخطاء برمجية أخرى تحتاج إلى إصلاح.
{alertWarning} تنبيه: لا تستخدمis_file()وحدها كبديل كامل عنtry/except. الفحص يقدّم رسالة مفهومة للمستخدم، لكن عملية القراءة نفسها يمكن أن تفشل لاحقًا بسبب الصلاحيات أو تغيير الملف أو المسار.
مثال عملي: قراءة الملفات النصية من مجلد
أحيانًا يكون هدفك فعلًا التعامل مع مجلد، لكنك تريد قراءة الملفات الموجودة بداخله. في هذه الحالة لا تستدعِ read_text() على المجلد نفسه؛ اعرض العناصر، ثم اقرأ الملفات فقط.
from pathlib import Path
folder = Path("reports")
if not folder.is_dir():
print("المجلد المطلوب غير موجود أو أن المسار ليس مجلدًا.")
else:
for item in folder.iterdir():
if item.is_file() and item.suffix.lower() == ".txt":
content = item.read_text(encoding="utf-8")
print(f"--- {item.name} ---")
print(content)
الكود السابق يتجاهل المجلد archive والملف chart.png، لأنه يقرأ العناصر التي تحقق شرطين معًا:
- العنصر ملف حقيقي:
item.is_file(). - امتداد الملف هو
.txt.
تستطيع لاحقًا تطوير المثال لقراءة ملفات CSV أو JSON، لكن لا تفترض أن كل عنصر داخل المجلد مناسب للقراءة بالطريقة نفسها.
الفرق بين IsADirectoryError وأخطاء قريبة منه
| الخطأ | معناه | مثال مختصر |
|---|---|---|
IsADirectoryError |
استخدمت مجلدًا في عملية تحتاج ملفًا. | Path("reports").read_text() |
FileNotFoundError |
المسار أو الملف المطلوب غير موجود. | Path("missing.txt").read_text() |
PermissionError |
لا توجد صلاحية كافية للوصول إلى الملف أو المجلد. | قراءة ملف محمي أو مفتوح بصلاحيات مقيدة. |
NotADirectoryError |
استخدمت ملفًا في عملية تحتاج مجلدًا. | محاولة استعراض ما داخل ملف كما لو كان مجلدًا. |
يمكنك الرجوع إلى حل خطأ FileNotFoundError عند فتح الملفات عندما لا يكون الملف موجودًا، وإلى حل خطأ PermissionError عند فتح الملفات عندما يكون المسار صحيحًا لكن المشكلة في الصلاحيات.
هل أستخدم os أم pathlib؟
يمكن حل المشكلة باستخدام os.path.isfile() وos.path.isdir() أيضًا، لكن pathlib مناسب جدًا للمبتدئ لأنه يجمع بناء المسار وفحصه وقراءته في كائن واحد.
import os
path = "reports"
if os.path.isfile(path):
with open(path, "r", encoding="utf-8") as file:
print(file.read())
elif os.path.isdir(path):
print("المسار مجلد وليس ملفًا.")
لا توجد مشكلة في استخدام os، خاصة إن كان مشروعك قديمًا ويستخدمه بالفعل. لكن لا تخلط الطريقتين بلا سبب داخل المثال نفسه؛ اختر أسلوبًا واضحًا وثابتًا.
أخطاء شائعة يجب تجنبها
1. فتح اسم المجلد بدل اسم الملف
لا تكتب Path("reports").read_text() إن كنت تريد قراءة ملف داخل المجلد. اكتب مسار الملف كاملًا مثل Path("reports") / "summary.txt".
2. الاعتماد على exists فقط
وجود المسار لا يثبت أنه ملف. استخدم is_file() أو is_dir() حسب ما تحتاجه.
3. استخدام except فارغ
try:
content = path.read_text()
except:
print("حدث خطأ.")
هذا الأسلوب يخفي سبب المشكلة. اكتب استثناءات محددة، مثل IsADirectoryError أو FileNotFoundError أو PermissionError، ثم اعرض رسالة مناسبة.
4. قراءة كل ما داخل المجلد بلا فلترة
داخل المجلد قد توجد صور ومجلدات فرعية وملفات مختلفة. افحص is_file() وفلتر الامتداد الذي تتوقعه قبل القراءة.
الخلاصة
- يظهر
IsADirectoryErrorعندما تطلب من بايثون تنفيذ عملية ملف على مسار يشير إلى مجلد. exists()يثبت وجود المسار فقط، لكنه لا يحدد نوعه.- استخدم
is_file()قبل قراءة ملف، وis_dir()قبل استعراض محتوى مجلد. - عند قراءة ملف داخل مجلد، ابنِ مسار الملف كاملًا باستخدام
Path("folder") / "file.txt". - تعامل مع الأخطاء المحددة داخل
try/exceptبدل التقاط كل الأخطاء برسالة عامة. - عند استعراض مجلد، اقرأ الملفات المطابقة فقط واترك المجلدات والامتدادات غير المناسبة.
{alertSuccess} قاعدة عملية: قبل تنفيذ أي قراءة أو كتابة، اسأل: هل أتعامل مع ملف أم مجلد؟ هذا السؤال البسيط يمنع كثيرًا من أخطاء المسارات في مشاريع بايثون.
مصادر رسمية للتوسع
- توثيق IsADirectoryError في بايثون
- توثيق pathlib وPath في بايثون
- توثيق مكتبة os للتعامل مع نظام التشغيل
أسئلة شائعة
ما معنى IsADirectoryError: [Errno 21] Is a directory؟
تعني أن Python حاولت تنفيذ عملية مخصصة لملف على مسار يشير إلى مجلد. افحص المسار قبل القراءة باستخدام is_file() وis_dir()، وتأكد أنك مررت اسم الملف الكامل لا اسم المجلد فقط.
كيف أحل IsADirectoryError عند استخدام open() أو Path.read_text()؟
إذا كان المسار مجلدًا، أضف اسم الملف المطلوب مثل Path("reports") / "summary.txt". وإذا كان المسار يأتي من مصدر متغير، افحصه باستخدام is_file() وتعامل مع IsADirectoryError داخل try/except عند الحاجة.
هل IsADirectoryError يعني أن المجلد غير موجود؟
لا. غالبًا يعني العكس: المسار موجود، لكنه مجلد بينما الكود كان يحتاج ملفًا. عندما لا يكون المسار موجودًا غالبًا ستواجه FileNotFoundError.
هل يكفي استخدام exists قبل read_text؟
لا. exists() لا يفرق بين الملف والمجلد. استخدم is_file() إذا كنت ستقرأ ملفًا، ثم تعامل مع الأخطاء المتوقعة عبر try/except.
كيف أقرأ ملفًا داخل مجلد؟
أنشئ مسارًا للملف نفسه، مثل Path("reports") / "summary.txt"، ثم تحقق من is_file() واقرأه باستخدام read_text() أو open().
هل أستخدم pathlib أم os لحل الخطأ؟
كلتاهما صالحتان. pathlib يقدم صياغة مرتبة لبناء المسارات وفحصها، بينما os.path مفيد في المشاريع التي تستخدمه مسبقًا. الأهم هو فحص ما إذا كان المسار ملفًا أو مجلدًا قبل العملية المناسبة.
لماذا أضع try وexcept بعد is_file؟
لأن الفحص يساعدك على إعطاء رسالة واضحة مبكرًا، لكنه لا يضمن أن القراءة ستنجح دائمًا. يمكن أن تتغير الصلاحيات أو حالة المسار قبل القراءة، لذلك يبقى التعامل المحدد مع الاستثناءات ضروريًا.



