I was reading a tweet about it yesterday and that didn’t stop me from pushing a code change in production with the same rookie mistake today. Consider this function:
# src.py
from __future__ import annotations
import logging
import time
from datetime import datetime
def log(
message: str,
/,
*,
level: str,
timestamp: str = datetime.utcnow().isoformat(),
) -> None:
logger = getattr(logging, level)
# Avoid f-string in logging as it's not lazy.
logger("Timestamp: %s \nMessage: %s\n" % (timestamp, message))
if __name__ == "__main__":
for _ in range(3):
time.sleep(1)
log("Reality can often be disappointing.", level="warning")
Here, the function log has a parameter timestamp that computes its default value using
the built-in datetime.utcnow().isoformat() method. I was under the impression that the
timestamp parameter would be computed each time when the log function was called.
However, that’s not what happens when you try to run it. If you run the above snippet,
you’ll get this instead: