Skipping over failed imports until they are needed (if ever)
There is one issue with your approach which is, in the case of multiple import failure, only the last one can be properly reported:

&gt;&gt;&gt; try:
...   from .foo import Foo
... except ImportError:
...   from .base_engine import Default as Foo
...   setattr(Foo, 'name', 'Foo')
... 
&gt;&gt;&gt; try:
...   from .bar import Bar
... except ImportError:
...   from .base_engine import Default as Bar
...   setattr(Bar, 'name', 'Bar')
... 
&gt;&gt;&gt; Bar()
Traceback (most recent call last):
…
ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.
&gt;&gt;&gt; Foo()
Traceback (most recent call last):
…
ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.


You instead want to generate a new class with the proper message each time.

Something along the line should do:

def mock_missing(name):
    def init(self, *args, **kwargs):
        raise ImportError(
            f'The class {name} you tried to call is not importable; '
            f'this is likely due to it not being installed.')
    return type(name, (), {'__init__': init})


Usage being:

try:
    from .foo import Foo
except ImportError:
    Foo = mock_missing('Foo')