Conditional imports in one line using `else`


(Gregory P. Smith) #1

Conditional imports are somewhat common in code that supports multiple platforms, or code that supports additional functionality when some extra bonus modules are available. Today this is often written as:

# pylint: disable=import-not-at-top
try:
  from monty.python.au import bruce
else:
  bruce = None

Four lines of code, plus a potential linter directive asking tooling not to whine about logic before imports or non top level imports.

What if we could simply write any variant of the following:

from monty.python.au import bruce else None
from monty.python.au import bruce else bruce = None
from monty.python.au import michael_baldwin as bruce else None
from monty.python.au import michael_baldwin as bruce else bruce = None
import michael_baldwin else None
import michael_baldwin else michael_baldwin = None
import michael_baldwin as bruce else bruce = None
import michael_baldwin as bruce else None
import monty.python.au else None  # confusing...
import monty.python.au else monty = None  # yuck

I’m using an else to convey the condition what to do on import failure instead of the try+except. I don’t really like that the name has to be repeated for the most natural reading but I dislike the way it reads without the name = expr to convey the conditional alternate name binding. I’d limit the else to only capturing ImportError though you could argue that it should be a broad except.

I’m also suggesting not supporting else on anything other than a simple single name import. import long.dotted.name could be ineligible for else because it feels awkward, though so long as the else name = expr syntax were chosen it’d still be comprehensible. Want to import multiple names from a package in a single import statement? I suggest not supporting else on that; though that should work fine via tuple assignment. having such things to not support is probably more complicated.

things to avoid? I don’t want anyone to write a conditional expression after the else. Or to allow anything other than simple = assignment if adopting that syntax (no augassign).

thoughts? a quick search didn’t turn this up as being proposed in the past but I didn’t look far.

unresolved thoughts. sometimes the alternate of a conditional import is a different import.

import json else import simplejson as json

Include nested conditional import statements as part of this?


(Petr Viktorin) #2

A change like this should have heftier motivation than replacing four lines of code with one.
A great example is PEP 380: yield from usually replaces just two lines, for v in g: yield v. If it was only that, it would have been rejected (at least under the old governance). The reason for adding yield from was that it also solves various more subtle problems. With then new command, the correct way to write it is also the most obvious – and that wasn’t true before.

I fear that’s not the case in your proposal. The import ... else ... doesn’t do anything more than the four lines it replaces. And it’s less flexible – the four lines can be trivially extended to things like:

  • Handling other kinds of errors than ImportError
  • Importing a fallback module instead (as you mentioned)
  • Choosing a module based on a condition (if for some reason you need to look before you leap instead of preparing to ask for forgiveness)
  • Having two optional modules, and importing either both or neither of them

If it’s the linter that bothers you, look into fixing your linter (and possibly into adjusting PEP 8 before that, to codify the right way to format conditional imports).