The Silent Failure That Took 20 Minutes to Debug
A lazy-loading config object was silently returning None for every attribute that didn't exist. No errors, no warnings — just quiet failure that propagated through the entire pipeline until a JSON serialization crash three layers deep revealed the problem.
The culprit? Confusing __getattr__ with __getattribute__.
Python gives you two hooks for intercepting attribute access, and they behave radically differently. One only fires when normal lookup fails. The other intercepts every single access, including methods you didn't even know your class was using. Mix them up and you get subtle bugs that only surface in production.
Here's what happens when you run this:
class LazyConfig:
def __getattribute__(self, name):
print(f"__getattribute__: {name}")
return object.__getattribute__(self, name)
def __getattr__(self, name):
print(f"__getattr__: {name}")
return None
config = LazyConfig()
config.database_url # What prints?
Most developers expect __getattr__ to fire. But both methods print — and in a specific order that reveals Python's attribute lookup chain.
Continue reading the full article on TildAlice
Top comments (0)