Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions verl/utils/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ def __del__(self):
self.logger["vemlp_wandb"].finish(exit_code=0)
if "tensorboard" in self.logger:
self.logger["tensorboard"].finish()
if "clearnml" in self.logger:
self.logger["clearnml"].finish()
if "clearml" in self.logger:
self.logger["clearml"].finish()
Comment on lines +164 to +165
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

While this change correctly fixes the typo for the clearml logger, the overall approach of using __del__ for resource cleanup is unreliable and should be avoided.

The __del__ method is not guaranteed to be called in Python under all circumstances (e.g., with circular references, or during interpreter shutdown). Relying on it for critical cleanup tasks like finalizing loggers can lead to data loss or experiments being left in an inconsistent state.

A much safer and more idiomatic Python pattern is to provide an explicit finish() or close() method on the Tracking class and use it as a context manager. This ensures that cleanup is performed deterministically.

I strongly recommend refactoring this cleanup logic out of __del__. Here is an example of how this could be implemented:

class Tracking:
    # ... existing __init__ and log methods ...

    def finish(self):
        if "wandb" in self.logger:
            self.logger["wandb"].finish(exit_code=0)
        if "swanlab" in self.logger:
            self.logger["swanlab"].finish()
        if "vemlp_wandb" in self.logger:
            self.logger["vemlp_wandb"].finish(exit_code=0)
        if "tensorboard" in self.logger:
            self.logger["tensorboard"].finish()
        if "clearml" in self.logger:
            self.logger["clearml"].finish()
        if "trackio" in self.logger:
            self.logger["trackio"].finish()
        if "file" in self.logger:
            self.logger["file"].finish()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.finish()

    def __del__(self):
        # As a fallback, but ideally finish() is called explicitly
        # or via the context manager. Consider logging a warning here
        # if the object was not explicitly finished.
        self.finish()

This would require consumers of the Tracking class to use a with statement or call finish() explicitly, guaranteeing proper cleanup.

if "trackio" in self.logger:
self.logger["trackio"].finish()
if "file" in self.logger:
Expand Down Expand Up @@ -218,7 +218,7 @@ def log(self, data, step):
)

def finish(self):
self._task.mark_completed()
self._task.close()


class FileLogger:
Expand Down