@@ -795,6 +795,44 @@ def setup():
795795 warnings .simplefilter ("always" )
796796
797797
798+ def _get_executors (topology ):
799+ executors = []
800+ for server in topology ._servers .values ():
801+ # Some MockMonitor do not have an _executor.
802+ executors .append (getattr (server ._monitor , '_executor' , None ))
803+ executors .append (topology ._Topology__events_executor )
804+ if topology ._srv_monitor :
805+ executors .append (topology ._srv_monitor ._executor )
806+ return [e for e in executors if e is not None ]
807+
808+
809+ def all_executors_stopped (topology ):
810+ running = [e for e in _get_executors (topology ) if not e ._stopped ]
811+ if running :
812+ print (' Topology %s has THREADS RUNNING: %s, created at: %s' % (
813+ topology , running , topology ._settings ._stack ))
814+ return False
815+ return True
816+
817+
818+ def print_unclosed_clients ():
819+ from pymongo .topology import Topology
820+ processed = set ()
821+ # Call collect to manually cleanup any would-be gc'd clients to avoid
822+ # false positives.
823+ gc .collect ()
824+ for obj in gc .get_objects ():
825+ try :
826+ if isinstance (obj , Topology ):
827+ # Avoid printing the same Topology multiple times.
828+ if obj ._topology_id in processed :
829+ continue
830+ all_executors_stopped (obj )
831+ processed .add (obj ._topology_id )
832+ except ReferenceError :
833+ pass
834+
835+
798836def teardown ():
799837 garbage = []
800838 for g in gc .garbage :
@@ -813,6 +851,10 @@ def teardown():
813851 c .drop_database ("pymongo_test_bernie" )
814852 c .close ()
815853
854+ # Jython does not support gc.get_objects.
855+ if not sys .platform .startswith ('java' ):
856+ print_unclosed_clients ()
857+
816858
817859class PymongoTestRunner (unittest .TextTestRunner ):
818860 def run (self , test ):
0 commit comments