Hi!
So when I do:
def log_engine_output(
    engine: Engine,
    fields: Dict[LOG_OP, Union[List[str], List[VisPlot], List[VisImg]]],
    epoch_num=None,
) -> None:
    """Log numerical fields in the engine output dictionary to stdout"""

    print(epoch_num)

    for mode in fields.keys():
        if (
            mode is LOG_OP.NUMBER_TO_VISDOM
            or mode is LOG_OP.VECTOR_TO_VISDOM
            or mode is LOG_OP.IMAGE_TO_VISDOM
        ):
            mode(
                engine,
                engine.state.vis,
                fields[mode],
                engine_attr="output",
                epoch_num=epoch_num,
            )
        else:
            mode(engine, fields[mode], engine_attr="output")


trainer.add_event_handler(
            Events.EPOCH_COMPLETED(every=1),
            log_engine_output,
            {
                # Log fields as message in logfile
                LOG_OP.LOG_MESSAGE: ["loss"],
                # Log fields as separate data files
                LOG_OP.SAVE_IN_DATA_FILE: ["loss"],
                # Plot fields to Visdom
                LOG_OP.NUMBER_TO_VISDOM: [
                    # First plot, key is "p1"
                    VisPlot(
                        "loss",
                        plot_key="p1",
                        split="nll_1",
                        title="Train loss: ",
                        x_label="Iters",
                        y_label="nll",
                        env=cfg.env,
                    ),
                ],
            },
            trainer.state.epoch,
        )

The epoch_num that is printed is always 0. That’s not right since it prints every epoch - why would this happen?
I’m also confusing about the structure of doing trainer.add_event_handler() vs. the decorator of
@trainer.on(Events.EPOCH_COMPLETED(every=1)).
I thought that the only difference between the decorator and the add_event_handler(some_function, variable1, variable2) was that with add_event_handler you could pass in the variables that are the arguments to the function (variable 1, variable 2), whereas with the decorator @train you can only pass in “engine” as your argument. Is this correct? If so, why isn’t passing the arguments working for me?
Also - what is the situation with add_event_handler automatically checking to see if the first argument it finds (after the function),is  “engine”? From the tutorial, “The first argument can be optionally engine, but not necessary.”.
def log_metrics(engine, title):
    print("Epoch: {} - {} accuracy: {:.2f}"
           .format(trainer.state.epoch, title, engine.state.metrics["acc"]))

@trainer.on(Events.EPOCH_COMPLETED)
def evaluate(trainer):
    with evaluator.add_event_handler(Events.COMPLETED, log_metrics, "train"):
        evaluator.run(train_loader)

Here it automatically passes “evaluator” as the “engine” argument to log_metrics. However, here:
trainer = Engine(update_model)

trainer.add_event_handler(Events.STARTED, lambda _: print("Start training"))
# or
@trainer.on(Events.STARTED)
def on_training_started(engine):
    print("Another message of start training")
# or even simpler, use only what you need !
@trainer.on(Events.STARTED)
def on_training_started():
    print("Another message of start training")

# attach handler with args, kwargs
mydata = [1, 2, 3, 4]

def on_training_ended(data):
    print("Training is ended. mydata={}".format(data))

trainer.add_event_handler(Events.COMPLETED, on_training_ended, mydata)

The first argument is not “engine”. Does this mean that somehow ignite checks the function passed into add_event_handler to see if the first argument is called “engine”? And if so, does the nomenclature have to be exactly called “engine” in order for it to pass the engine that add_event_handler is attached to to the function?
Thanks!