@@ -2899,19 +2899,16 @@ cdef class Model:
28992899 self .includeEventhdlr(event_handler, name, description)
29002900
29012901 def __del__ (self ):
2902- """ Free SCIP before the cyclic GC clears Python references .
2902+ """ Free SCIP when the last external reference to the Model is dropped .
29032903
2904- __del__ (tp_finalize) runs before tp_clear, so _plugins and other
2905- Python attributes are still intact. This lets SCIPfree call plugin
2906- callbacks (consfree, eventexit, etc.) safely. The GC handles breaking
2907- the circular references afterwards via tp_clear.
2904+ Runs before the garbage collector clears the Model's attributes, so
2905+ plugin callbacks can still reach a live ``self.model``. See
2906+ ``Model.free`` for the full lifecycle policy.
29082907 """
29092908 self ._free_scip_instance()
29102909
29112910 def __dealloc__ (self ):
2912- # Safety net: if __del__ didn't run (e.g. interpreter shutdown),
2913- # free SCIP here. Plugin callbacks may not work at this point since
2914- # tp_clear may have already cleared _plugins.
2911+ """ Fallback SCIP cleanup if ``__del__`` did not already free the instance."""
29152912 if self ._scip is not NULL and self ._freescip:
29162913 SCIPfree(& self ._scip)
29172914
@@ -3089,11 +3086,28 @@ cdef class Model:
30893086 PY_SCIP_CALL(SCIPfreeProb(self ._scip))
30903087
30913088 def free (self ):
3092- """ Explicitly free SCIP instance and break circular references with plugins.
3093-
3094- This method should be called when you want to ensure deterministic cleanup.
3095- All SCIP callbacks (consfree, eventexit, etc.) are executed during this call.
3096- After calling this method, the Model object should not be used anymore.
3089+ """ Explicitly free the SCIP instance and release plugin references.
3090+
3091+ Every included plugin holds a strong reference to its Model via
3092+ ``self.model``, and the Model holds strong references to every plugin
3093+ via ``Model._plugins``. This cycle is intentional: it keeps both sides
3094+ alive during SCIP teardown so callbacks such as ``eventexit`` and
3095+ ``consfree`` can safely reach ``self.model``.
3096+
3097+ The cycle is broken in one of two ways:
3098+
3099+ * Implicitly, via ``Model.__del__`` when the last external reference to
3100+ the Model is dropped. ``__del__`` runs before the garbage collector
3101+ (GC) clears attributes, so callbacks still see a live ``self.model``.
3102+ Timing is at the GC's discretion.
3103+ * Explicitly, by calling this method. SCIP is freed immediately,
3104+ teardown callbacks fire before ``free`` returns, and the plugin list
3105+ is cleared. Use this when deterministic cleanup is required. After
3106+ ``free`` the Model must not be used further.
3107+
3108+ The solve itself is not affected by which path runs. ``free`` only
3109+ controls when memory and resources are released, and when teardown
3110+ callbacks execute.
30973111 """
30983112 self ._free_scip_instance()
30993113
0 commit comments