سؤال

لدي وحدة تخزين مؤقت urllib2 ، والتي تعطل بشكل متقطع بسبب الكود التالي:

if not os.path.exists(self.cache_location):
    os.mkdir(self.cache_location)

المشكلة هي أنه بحلول الوقت الذي يتم فيه تنفيذ السطر الثاني ، قد يوجد المجلد ، وسيخطئ:

  File ".../cache.py", line 103, in __init__
    os.mkdir(self.cache_location)
OSError: [Errno 17] File exists: '/tmp/examplecachedir/'

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

يمكن العثور على الرمز (قبل أن أحاول إصلاح الخطأ) هنا ، على جيثب

لا يمكنني استخدام tempfile.mkstemp, ، لأنه يحل حالة السباق باستخدام دليل مسمى عشوائيًا (المصدر tempfile.py هنا) ، والتي من شأنها أن تهزم الغرض من ذاكرة التخزين المؤقت.

لا أريد ببساطة تجاهل الخطأ ، حيث يتم رفع خطأ Errno 17 نفسه إذا كان اسم المجلد موجودًا كملف (خطأ مختلف) ، على سبيل المثال:

$ touch blah
$ python
>>> import os
>>> os.mkdir("blah")
Traceback (most recent call last):
  File "", line 1, in 
OSError: [Errno 17] File exists: 'blah'
>>>

لا أستطيع استخدام threading.RLock كما يسمى الكود من عمليات متعددة.

لذا ، حاولت كتابة قفل بسيط قائم على الملف (يمكن العثور على هذا الإصدار هنا) ، لكن هذا لديه مشكلة: إنه يخلق مستوى Lockfile لأعلى ، لذلك /tmp/example.lock ل /tmp/example/, ، الذي ينهار إذا كنت تستخدم /tmp/ كداحة ذاكرة التخزين المؤقت (كما تحاول صنعها /tmp.lock)..

باختصار ، أحتاج إلى ذاكرة التخزين المؤقت urllib2 ردود على القرص. للقيام بذلك ، أحتاج إلى الوصول إلى دليل معروف (إنشاءه ، إذا لزم الأمر) ، بطريقة آمنة متعددة المعالجة. يحتاج إلى العمل على OS X و Linux و Windows.

أفكار؟ الحل البديل الوحيد الذي يمكنني التفكير فيه هو إعادة كتابة وحدة ذاكرة التخزين المؤقت باستخدام تخزين SQLite3 ، بدلاً من الملفات.

هل كانت مفيدة؟

المحلول

في Python 3.x ، يمكنك استخدام os.makedirs(path, exists_ok=True), ، والتي لن تثير أي استثناء في حالة وجود هذا الدليل. سوف ترفع FileExistsError: [Errno 17] إذا كان هناك ملف بنفس الاسم مثل الدليل المطلوب (path).

تحقق من ذلك مع:

import os

parent = os.path.dirname(__file__)

target = os.path.join(parent, 'target')

os.makedirs(target, exist_ok=True)
os.makedirs(target, exist_ok=True)

os.rmdir(target)

with open(target, 'w'):
    pass

os.makedirs(target, exist_ok=True)

نصائح أخرى

بدلاً من

if not os.path.exists(self.cache_location):
    os.mkdir(self.cache_location)

يمكنك أن تفعل

try:
    os.makedirs(self.cache_location)
except OSError:
    pass

كما ستنتهي بنفسه وظائف.

إخلاء المسئولية: لا أعرف كيف قد يكون هذا البيثون.


استخدام SQLite3, ربما تكون مبالغة قليلاً ، ولكنها ستضيف أ كثيرا من الوظائف والمرونة لحالة الاستخدام الخاصة بك.

إذا كان عليك القيام بالكثير من "الاختيار" ، الإدراج المتزامن والتصفية ، فمن الجيد استخدامه SQLite3, ، لأنه لن يضيف الكثير من التعقيد على الملفات البسيطة (يمكن القول أنه يزيل التعقيد).


إعادة قراءة سؤالك (والتعليقات) يمكنني فهم مشكلتك بشكل أفضل.

ما هو احتمال أن أ ملف يمكن أن تخلق نفس حالة السباق؟

إذا كان صغيرًا بما يكفي ، فسأفعل شيئًا مثل:

if not os.path.isfile(self.cache_location):
    try:
        os.makedirs(self.cache_location)
    except OSError:
        pass

أيضا ، قراءة الكود الخاص بك ، سأتغير

else:
    # Our target dir is already a file, or different error,
    # relay the error!
    raise OSError(e)

ل

else:
    # Our target dir is already a file, or different error,
    # relay the error!
    raise

لأنه حقًا ما تريده ، فإن Python لإعادة الاستثناء نفسه بالضبط (فقط nitpicking).


شيء آخر ، قد يكون هذه يمكن أن يكون مفيدًا لك (يشبه UNIX فقط).

كان الرمز الذي انتهى به الأمر:

import os
import errno

folder_location = "/tmp/example_dir"

try:
    os.mkdir(folder_location)
except OSError as e:
    if e.errno == errno.EEXIST and os.path.isdir(folder_location):
        # File exists, and it's a directory,
        # another process beat us to creating this dir, that's OK.
        pass
    else:
        # Our target dir exists as a file, or different error,
        # reraise the error!
        raise

هل يمكنك التقاط الاستثناء ثم اختبار ما إذا كان الملف موجودًا كدليل أم لا؟

عندما يكون لديك ظروف سباق في كثير من الأحيان EAFP (أسهل في السؤال عن الإذن) يعمل بشكل أفضل على أن LBYL (انظر قبل أن تقفز)

خطأ الفحص الاستراتيجيات

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top