def test_attach_model_callbacks():
    """Test that the callbacks defined in the model and through Trainer get merged correctly."""

    def _attach_callbacks(trainer_callbacks, model_callbacks):
        model = LightningModule()
        model.configure_callbacks = lambda: model_callbacks
        has_progress_bar = any(isinstance(cb, ProgressBar) for cb in trainer_callbacks + model_callbacks)
        trainer = Trainer(
            enable_checkpointing=False,
            enable_progress_bar=has_progress_bar,
            enable_model_summary=False,
            callbacks=trainer_callbacks,
        )
        trainer.strategy._lightning_module = model
        cb_connector = _CallbackConnector(trainer)
        cb_connector._attach_model_callbacks()
        return trainer

    early_stopping1 = EarlyStopping(monitor="red")
    early_stopping2 = EarlyStopping(monitor="blue")
    progress_bar = TQDMProgressBar()
    lr_monitor = LearningRateMonitor()
    grad_accumulation = GradientAccumulationScheduler({1: 1})

    # no callbacks
    trainer = _attach_callbacks(trainer_callbacks=[], model_callbacks=[])
    assert trainer.callbacks == []

    # callbacks of different types
    trainer = _attach_callbacks(trainer_callbacks=[early_stopping1], model_callbacks=[progress_bar])
    assert trainer.callbacks == [early_stopping1, progress_bar]

    # same callback type twice, different instance
    trainer = _attach_callbacks(
        trainer_callbacks=[progress_bar, EarlyStopping(monitor="red")],
        model_callbacks=[early_stopping1],
    )
    assert trainer.callbacks == [progress_bar, early_stopping1]

    # multiple callbacks of the same type in trainer
    trainer = _attach_callbacks(
        trainer_callbacks=[
            LearningRateMonitor(),
            EarlyStopping(monitor="yellow"),
            LearningRateMonitor(),
            EarlyStopping(monitor="black"),
        ],
        model_callbacks=[early_stopping1, lr_monitor],
    )
    assert trainer.callbacks == [early_stopping1, lr_monitor]

    # multiple callbacks of the same type, in both trainer and model
    trainer = _attach_callbacks(
        trainer_callbacks=[
            LearningRateMonitor(),
            progress_bar,
            EarlyStopping(monitor="yellow"),
            LearningRateMonitor(),
            EarlyStopping(monitor="black"),
        ],
        model_callbacks=[early_stopping1, lr_monitor, grad_accumulation, early_stopping2],
    )
    assert trainer.callbacks == [progress_bar, early_stopping1, lr_monitor, grad_accumulation, early_stopping2]

    class CustomProgressBar(TQDMProgressBar): ...

    custom_progress_bar = CustomProgressBar()
    # a custom callback that overrides ours
    trainer = _attach_callbacks(trainer_callbacks=[progress_bar], model_callbacks=[custom_progress_bar])
    assert trainer.callbacks == [custom_progress_bar]

    # edge case
    bare_callback = Callback()
    trainer = _attach_callbacks(trainer_callbacks=[bare_callback], model_callbacks=[custom_progress_bar])
    assert trainer.callbacks == [bare_callback, custom_progress_bar]
Test that the callbacks defined in the model and through Trainer get merged correctly.
tests/tests_pytorch/trainer/connectors/test_callback_connector.py