I basically agree with many of your points. I have to admit that I am
likely to be biased from my view, since I got hindered by the current
behavior in that scenario. As you correctly stated,

     import module
     from module import name
     assert name is module.name
and

      from module import spam
      from module import eggs
things can become confusing, especially if you are reading the code as a
third person that doesn't know someone might have added stuff to path
resulting into this conflict. Actually I am surprised that someone
actually things it through that far and comes up out of the nothing with
those examples. Makes me already a bit happy ;)

However, I don't see where

     # Support many different versions of Python, or old versions
     # of the module, or drop-in replacement of the module.
     try:
         from module import name
     except ImportError:
         from fallback_module import spam as name
might change a actual thing.
Case 1: Lets say "module" does not exist -> both fail
Case 2: Exactly one "module" exists but it does not contain "name" ->
both fail
Case 3: Multiple "module" exist. At least one having "name" inside
    - Current Variant suceeds if and only if the first touched "module"
does contain "name", otherwise fail
    - Proposed Variant will fail if and only if NO "module" at all
contains "name"

    --> Why the first variant would be anyhow more correct than the
second? A later module could have the "name" which you actually wanted,
but you don't load it and falsly do a fallback

Still, I see in sum that this at best is a trade-off, which one could
argue pros and cons.

So lets take a step back and maybe agree that the "shadowing" behaviour
may come in unexpected also. The thing I made (which I right now changed
completely thanks to the discussions to a setup.py X pip install -e
solution that basically solves it) other people will do also. Given
you're not too familiar with development installs or python setup at
all, this is a thing you might not even know of or see as an unneeded
obstacle. Hence, you will do some supposedly straight-forward thing like
making your libs available in path. On top you might not even know that
some dependency of a dependencies dependency might shadow your imports
at all or tempers also with the path on its own.

To conclude that, without changing the behaviour, Python might at least
recognize such a shadowing case and spill out a warning if something
shadows something else (Like "Unreachable module") since it obviously
could find it out for example with your proposed import algorithm
variant. Someone then could actually google it, find the reasoning and
would stumple about ways to fix his/her presumably bad practice.

Thanks anyways for taking the time thinking through the implications
clarifying your points!

Am 29.10.2019 um 23:41 schrieb Steven D'Aprano:
On Tue, Oct 29, 2019 at 01:26:36PM -0000, mer...@gmx.net wrote:
Yes :)

That looks similiar to my problem, which i fixed for the moment of writing by 
renaming one folder.
Basically, I would prefer if Python would not treat nth-level import fails 
different than first-level fails.
I think you mean that you would prefer if Python *would* treat nth-level
import failures differently. If the first level fails, the import search
stops immediately and raises; you want the import search to continue if
the second level fails.


Might seem like a small thing, but in my opinion it would:
- keep the behaviour straight forward
- Correct the error messages, since from the perspective of the user the import 
actually DOES exist
- Does not break anything currently working
- Makes me happy ;)
I disagree with all but the last.

Think about the behaviour of ``from module import name`` in pure
Python. Currently, it is straightforward to explain:

     try to import module, or fail if it doesn't exist;
     name = module.name, or fail if the name doesn't exist.

With your behaviour, it becomes something complicated:

     try to import module, or fail if it doesn't exist;
     remember where we are in the module import search path;
     try to set name = module.name;
     if that succeeds, return;
     else go back to line 1, but picking up the search from
     where we left off.

Re your point 2, I'm a user, and from my perspective, if module.name
doesn't exist in the first module found, then it doesn't exist until I
move or rename one of the clashing modules. That's how shadowing works,
and I like it that way because it makes it easy to reason about what my
code will do.

But the critical point is #3. Any change in behaviour could break
working code, and this could too:


     # Support many different versions of Python, or old versions
     # of the module, or drop-in replacement of the module.
     try:
         from module import name
     except ImportError:
         from fallback_module import spam as name

Same for the similar idiom:

     try:
         from module import name
     except ImportError:
         def name(arg):
             # re-implement basic version as a fallback


I use those two idioms frequently. The last thing I want is for the
behaviour of import to change, which would potentially change the
behaviour above from what's tested and works to something which might
pick up *the wrong module* from what I'm expecting it to pick up.

You are asking us to change the behaviour of import in the case of
shadowing, to support a use-case which is (in my, and probably most
peoples, opinion) very poor practice. That has some pretty surprising
consequences:

      from module import spam
      from module import eggs

You would expect that spam and eggs both come from the same module,
right? Surprise! No they don't, with your proposal they might come from
different modules. That's going to play havok with debugging.

     import module
     from module import name
     assert name is module.name

You would expect that assertion to be true, right? (Importing doesn't
copy objects.) Surprise! The assertion can raise AttributeError:
module.name might not exist even if ``from module import name``
succeeds.

I expect that with a bit more thought I could come up with some more
scenarios where the behaviour of Python programs could change in very
surprising ways.

So I will argue against this proposed change.


_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NFMIWCDA4FZEV5RKAJ2PA7BHNTH7PBUC/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to