Imports can be aliased, so why allow this mismatch at all? PyPI should have enforced that each package contains one top-level module whose name is identical to the name used to install it.
pillow is imported as `PIL` because it's a fork of the original PIL[1]. There's a very strong argument that Python's ability to retain the same import name across package name changes like that is a valuable source of flexibility that has benefited the ecosystem as a whole.
As in, `import pillow as PIL`?
As in, not changing your imports at all, and just changing your dependency from PIL to pillow. This has two substantial advantages:
1. You only have to change one line (the dependency), not an indefinite number of source files. This is less of an issue now that the Python community has high-quality refactoring tools, but it's still the past of least resistance.
2. More importantly: `import pillow as PIL` is not referentially transparent: the `PIL` binding that it introduces is a `module` object, but that object can't be used in subsequent imports. In other words, blindly performing an `import X as Y` refactor would break code like this:
import PIL
from PIL import whatever
You can observe this for yourself locally: >>> import ssl as lol
>>> from lol import CERT_NONE
ModuleNotFoundError: No module named 'lol'
>>> from ssl import CERT_NONE
This is arguably a defect in Python's import and module machinery, but that's how it currently is. Renaming the dependency and keeping the module name is far less fraught. import PIL
does not make PIL.Image available. What the hell else do you expect me to do with PIL? Why isn't PIL.Image included in importing PIL? You have to explicitly do either of these import PIL.Image
from PIL import Image(Understanding the difference between packages, module hierarchies, and module attributes is table stakes for architecting a large Python package correctly. PIL almost certainly does this to prevent hard-to-debug circular imports elsewhere in their codebase.)
I wish it was clearer sometimes what was a module, and what was an attribute in the core import syntax. `import foo; foo.bar` only breaks if it's a module, and `import foo.bar` only breaks if it's an attribute. If you do `from foo import bar`, the syntax works with both.
If you see `os.path.basename` what could `os.path` be? It would be a module most of the time because it's written with lowercase. `itertools.chain.from_iterable` [1] would be a notable exception.
[1]: https://docs.python.org/3/library/itertools.html#itertools.c...
I don't think that is a thing that happens in real life.
* Practically, one package is associated with exactly one github repository, sometimes a few. You would see implementation switching from JavaScript to TypeScript, but almost never from python to Go. Normally people start a brand new project for that kind of thing. * The reality is that each language has its own library ecosystem, and people reinvent the wheel at least once for each language. I wish we live in a world where you could save the effort, instead implement everything only once and it runs efficiently and has idiomatic APIs everywhere. But that's not how it works. If you create a package for a language, that's it. You could reimplement the same thing like by line in another language, but that would be a different package for that language.
That's the neat part, it's not! You can distribute basically any kind of data with pip, within reason. Iirc Cmake can be pip-installed.
I guess though that there could be an issue with some dependencies being written against PIL and others being written against pillow?
Or else I'm going to start writing some python packages with ".js" in their name.