Simplified Dynamic Import API

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.

I’m not sure what you’re trying to accomplish. Why not just try the import and catch the exception if it can’t be imported?

1 Like

There is the function:

__import__

If the logging library supports it, you can set the NO_COLOR=1 env var on production and it’ll disable colour. It looks like you’re using Rich, in which case TTY_COMPATIBLE=0 and TERM=dumb are some of the other options it supports.

This is currently functioning python code that does the exact same thing; Where is the issue with it?

try:
    from rich import Logger as logger
except ImportError:
    pass
else:
    logger.activate()