@@ -839,6 +839,7 @@ def lock(
839839 blocking_timeout : Optional [float ] = None ,
840840 lock_class : Optional [Type [Lock ]] = None ,
841841 thread_local : bool = True ,
842+ raise_on_release_error : bool = True ,
842843 ) -> Lock :
843844 """
844845 Return a new Lock object using key ``name`` that mimics
@@ -885,6 +886,11 @@ def lock(
885886 thread-1 would see the token value as "xyz" and would be
886887 able to successfully release the thread-2's lock.
887888
889+ ``raise_on_release_error`` indicates whether to raise an exception when
890+ the lock is no longer owned when exiting the context manager. By default,
891+ this is True, meaning an exception will be raised. If False, the warning
892+ will be logged and the exception will be suppressed.
893+
888894 In some use cases it's necessary to disable thread local storage. For
889895 example, if you have code where one thread acquires a lock and passes
890896 that lock instance to a worker thread to release later. If thread
@@ -902,6 +908,7 @@ def lock(
902908 blocking = blocking ,
903909 blocking_timeout = blocking_timeout ,
904910 thread_local = thread_local ,
911+ raise_on_release_error = raise_on_release_error ,
905912 )
906913
907914
@@ -1596,18 +1603,24 @@ async def _execute(
15961603 result .args = (msg ,) + result .args [1 :]
15971604 raise result
15981605
1599- default_node = nodes .get (client .get_default_node ().name )
1600- if default_node is not None :
1601- # This pipeline execution used the default node, check if we need
1602- # to replace it.
1603- # Note: when the error is raised we'll reset the default node in the
1604- # caller function.
1605- for cmd in default_node [1 ]:
1606- # Check if it has a command that failed with a relevant
1607- # exception
1608- if type (cmd .result ) in self .__class__ .ERRORS_ALLOW_RETRY :
1609- client .replace_default_node ()
1610- break
1606+ default_cluster_node = client .get_default_node ()
1607+
1608+ # Check whether the default node was used. In some cases,
1609+ # 'client.get_default_node()' may return None. The check below
1610+ # prevents a potential AttributeError.
1611+ if default_cluster_node is not None :
1612+ default_node = nodes .get (default_cluster_node .name )
1613+ if default_node is not None :
1614+ # This pipeline execution used the default node, check if we need
1615+ # to replace it.
1616+ # Note: when the error is raised we'll reset the default node in the
1617+ # caller function.
1618+ for cmd in default_node [1 ]:
1619+ # Check if it has a command that failed with a relevant
1620+ # exception
1621+ if type (cmd .result ) in self .__class__ .ERRORS_ALLOW_RETRY :
1622+ client .replace_default_node ()
1623+ break
16111624
16121625 return [cmd .result for cmd in stack ]
16131626
0 commit comments