On 28.06.2017 15:26, Nick Coghlan wrote:
1. In 3.3+ you can just catch ImportError, check "exc.name", and
re-raise if it's not for the module you care about

I see, didn't know that one. I gave it a try and it's not 100% the behavior I have expected, but one could workaround if the valid package structure is known. Not sure for certain.

"from foo.bar.baz import abc" can yield to "exc.name" being one of "foo", "foo.bar" or "foo.bar.baz". Not perfect but sort of doable.

2. There's a reasonable case to be made that importlib should include
an ImportError -> RuntimeError conversion around the call to
loader.exec_module (in the same spirit as PEP 479). That way, in:

    try:
        import myspeciallib
    except ImportError:
        import fallbacks.MySpecialLib as myspeciallib

any caught ImportError would relate to "myspeciallib", while uncaught
ImportErrors arising from *executing* "myspeciallib" will be converted
to RuntimeError, with the original ImportError as their __cause__.

Generally changing the behavior for ImportError doesn't sound like it would work for all projects out there.

For fallback imports, I am on your side, that's a real use case which can be solved by changing the behavior of ImportErrors. But for all imports? I don't know if that's good idea.

[People should use tools, guard against bugs and try to avoid mistakes.]

Sure, but I don't see who this can help, if I use third-party code. The cases, which I described in the original post, were simple cases, where we catch too many exception. So, I don't even have the chance to see the error, to file a bug report, to issue a pull request, etc. etc.

The cases I'm interested in are the ones where you're either
developing some kind of framework and you need to code that framework
defensively to guard against unexpected failures in the components
you're executing (e.g. exec_module() in the PEP 451 import protocol),
or else you're needing to adapt between two different kinds of
exception reporting protocol (e.g. KeyError to AttributeError and
vice-versa).

I am unsure what you mean by those abstract words "framework" and "components". But let me state it in different words: there are raisers and catchers which do the respective thing with exceptions.

If you control the code on both sides, things are easy to change. Pre-condition: you know the bug in the first place, which is hard when you catch too much.
If you control the raiser only, it doesn't help to say: "don't make mistakes, configure systems right, code better, etc." People will make mistakes, systems will be misconfigured, linters don't find everything, etc.
If you control the catcher only, you definitely want to narrow down the amount of caught exceptions as far as possible. This was the original intend of this thread IIRC. This way you help to discover bugs in raising code. Addition benefit, you catching code reacts only to the right exceptions.


One word about frameworks here. Django, for instance, is on both sides. The template engine is mostly on the catchers side, whereas the database layer is on the raisers side.


I get the feeling that the solutions presented here are way too complicated and error-prone. My opinion on this topic still is that catching exceptions is not mandatory. Nobody is forced to do it and it's even better to let exceptions bubble up to visualize bugs. If one still needs to catch them, he should only catch those he really, really needs to catch and nothing more. If this cannot be improved sensibly, well, so be it. Although I still don't find the argument presented against "catching shallow exception" a little bit too abstract compared to the practical benefit. Maybe, there's a better solution, maybe not.


Regards,
Sven