@@ -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
@@ -1597,18 +1604,24 @@ async def _execute(
15971604 result .args = (msg ,) + result .args [1 :]
15981605 raise result
15991606
1600- default_node = nodes .get (client .get_default_node ().name )
1601- if default_node is not None :
1602- # This pipeline execution used the default node, check if we need
1603- # to replace it.
1604- # Note: when the error is raised we'll reset the default node in the
1605- # caller function.
1606- for cmd in default_node [1 ]:
1607- # Check if it has a command that failed with a relevant
1608- # exception
1609- if type (cmd .result ) in self .__class__ .ERRORS_ALLOW_RETRY :
1610- client .replace_default_node ()
1611- break
1607+ default_cluster_node = client .get_default_node ()
1608+
1609+ # Check whether the default node was used. In some cases,
1610+ # 'client.get_default_node()' may return None. The check below
1611+ # prevents a potential AttributeError.
1612+ if default_cluster_node is not None :
1613+ default_node = nodes .get (default_cluster_node .name )
1614+ if default_node is not None :
1615+ # This pipeline execution used the default node, check if we need
1616+ # to replace it.
1617+ # Note: when the error is raised we'll reset the default node in the
1618+ # caller function.
1619+ for cmd in default_node [1 ]:
1620+ # Check if it has a command that failed with a relevant
1621+ # exception
1622+ if type (cmd .result ) in self .__class__ .ERRORS_ALLOW_RETRY :
1623+ client .replace_default_node ()
1624+ break
16121625
16131626 return [cmd .result for cmd in stack ]
16141627
0 commit comments