I think the reason there's nothing in the standard library is because for it to be there it would need to make cast iron access guarantees. To provide anything less would give a false sense of security that could lead to just as many concurrency issues.
It's also nearly impossible to make these guarantees, without making substantial performance sacrifices. As such, it is left up to the user to consider how they will manage concurrency issues. This is in line with one of Python's the philosophies of "we're all consenting adults". That is, if you're writing a class I think it's reasonable that you should know which attributes you need to acquire a lock before accessing the attribute. Or, if you're really that concerned, write a wrapper/proxy class that controls all access to the underlying object.
With your example there are a number of ways in which the target object could accidentally escape. If the programmer isn't paying enough attention to the code they're writing/maintaining, then this HiddenLock
could provide that false sense of security. For instance:
with the_lock as obj:
pass
obj.func() # erroneous
with the_lock as obj:
return obj.func() # possibly erroneous
# What if the return value of `func' contains a self reference?
with the_lock as obj:
obj_copy = obj[:]
obj_copy[0] = 2 # erroneous?
This last one is particularly pernicious. Whether this code is thread safe depends not on the code within the with block, or even the code after the block. Instead, it is the implementation of the class of obj
that will mean this code is thread safe or not. For instance, if obj
is a list
then this is safe as obj[:]
creates a copy. However, if obj
is a numpy.ndarray
then obj[:]
creates a view and so the operation is unsafe.
Actually, if the contents of obj
were mutable then this could be unsafe as regardless (eg. obj_copy[0].mutate()
).