Manage file locking using the 'with' statement

This is clearer and more exception-safe.

(imported from commit b67641b05da9dbf8e5a887f398bac81ab5985cf3)
This commit is contained in:
Keegan McAllister
2012-10-31 13:38:59 -04:00
parent af36b437b6
commit 1ea92c0953
2 changed files with 38 additions and 8 deletions

View File

@@ -0,0 +1,36 @@
"""
Context managers, i.e. things you can use with the 'with' statement.
"""
import fcntl
from os import path
from contextlib import contextmanager
@contextmanager
def flock(lockfile, shared=False):
"""Lock a file object using flock(2) for the duration of a 'with' statement.
If shared is True, use a LOCK_SH lock, otherwise LOCK_EX."""
fcntl.flock(lockfile, fcntl.LOCK_SH if shared else fcntl.LOCK_EX)
try:
yield
finally:
fcntl.flock(lockfile, fcntl.LOCK_UN)
@contextmanager
def lockfile(filename, shared=False):
"""Lock a file using flock(2) for the duration of a 'with' statement.
If shared is True, use a LOCK_SH lock, otherwise LOCK_EX.
The file is given by name and will be created if it does not exist."""
if not path.exists(filename):
with open(filename, 'w') as lock:
lock.write('0')
# TODO: Can we just open the file for writing, and skip the above check?
with open(filename, 'r') as lock:
with flock(lock, shared=shared):
yield

View File

@@ -6,13 +6,13 @@ import base64
import calendar import calendar
from zephyr.lib.cache import cache_with_key from zephyr.lib.cache import cache_with_key
from zephyr.lib.initial_password import initial_password, initial_api_key from zephyr.lib.initial_password import initial_password, initial_api_key
import fcntl
import os import os
import simplejson import simplejson
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
from zephyr.lib import bugdown from zephyr.lib import bugdown
from zephyr.lib.bulk_create import batch_bulk_create from zephyr.lib.bulk_create import batch_bulk_create
from zephyr.lib.avatar import gravatar_hash from zephyr.lib.avatar import gravatar_hash
from zephyr.lib.context_managers import lockfile
import requests import requests
from django.contrib.auth.models import UserManager from django.contrib.auth.models import UserManager
from django.utils import timezone from django.utils import timezone
@@ -473,15 +473,9 @@ def get_user_profile_by_id(uid):
# Store an event in the log for re-importing messages # Store an event in the log for re-importing messages
def log_event(event): def log_event(event):
assert("timestamp" in event) assert("timestamp" in event)
if not os.path.exists(settings.MESSAGE_LOG + '.lock'): with lockfile(settings.MESSAGE_LOG + '.lock'):
with open(settings.MESSAGE_LOG + '.lock', 'w') as lock:
lock.write('0')
with open(settings.MESSAGE_LOG + '.lock', 'r') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
with open(settings.MESSAGE_LOG, 'a') as log: with open(settings.MESSAGE_LOG, 'a') as log:
log.write(simplejson.dumps(event) + '\n') log.write(simplejson.dumps(event) + '\n')
fcntl.flock(lock, fcntl.LOCK_UN)
def log_message(message): def log_message(message):
if not message.sending_client.name.startswith("test:"): if not message.sending_client.name.startswith("test:"):