We have a class of bug in our application which is caused often by importing a logging library we don’t install on production because this logging library spits out ANSI Color codes which development supports and production does not.
You can solve this by nesting the import in an environmental check which is fragile because what if it’s not installed locally ? if you need it one env but not the other. You also have the options of structuring the code in try / except blocks or using the dynamic import lib module.
But this code is not the most rememberable:
import importlib.util
import sys
def is_module_installed(module_name):
“”“Checks if a module is installed without importing it.”“”
# Optional: Check if it’s a built-in module first
if module_name in sys.builtin_module_names:
return True
spec = importlib.util.find_spec(module_name)
return spec is not None
# — Example Usage — print(is_module_installed(‘json’))
# True (Standard library) print(is_module_installed(‘requests’))
# True (If you have it installed via pip) print(is_module_installed(‘fake_module’))
# False
```
This is a common enough issue that I’ve seen other languages solve, i was thinking we could make this more ergonomic:
Option 1: Javascript Inspired
Python gains a import function which the compiler knows is a dynamic call on account of it having parenthesis after the import
if logger := import('rich.Logger'):
logger.activate()
Option 2: Import can be an expression
if logger := import rich from logger;
logger.activate()
To me the first one has issues with the keyword contention and I can’t figure out a better way to handle that, perhaps Core Team have pointers here ?
The second doesn’t introduce anything new but might not be desired as it makes dynamic path names impossible. However it might be the right balance.