@@ -795,6 +795,44 @@ def setup():
795
795
warnings .simplefilter ("always" )
796
796
797
797
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
+
798
836
def teardown ():
799
837
garbage = []
800
838
for g in gc .garbage :
@@ -813,6 +851,10 @@ def teardown():
813
851
c .drop_database ("pymongo_test_bernie" )
814
852
c .close ()
815
853
854
+ # Jython does not support gc.get_objects.
855
+ if not sys .platform .startswith ('java' ):
856
+ print_unclosed_clients ()
857
+
816
858
817
859
class PymongoTestRunner (unittest .TextTestRunner ):
818
860
def run (self , test ):
0 commit comments