The from .packages.six.moves.http_client import ...
expression causes .packages.six
to be loaded first. Python always loads all packages in a nested package reference to a module.
So .packages.six.moves.http_client
causes Python first to look for urllib3.packages
, then for urllib3.packages.six
, and so on. The import machinery does so by looking for the full name in sys.modules
, and if it is not there, triggers a module search and load for each.
The first time this happens, sys.modules['urllib3.packages.six']
doesn't exist yet, the import machinery finds the file urllib3/packages/six.py
, imports that, before it'll look for more names.
And, as you discovered, the very act of importing the six.py
module file, causes that module to add sys.modules['urllib3.packages.six.moves']
and further references to standard library modules.
Python's import machinery is quite a complex beast; the Python reference documentation covers this comprehensively in The import system; the specific entries to look for are:
A direct call to __import__()
performs only the module search and, if found, the module creation operation. While certain side-effects may occur, such as the importing of parent packages, and the updating of various caches (including sys.modules), only the import statement performs a name binding operation.
and under Regular packages
Importing parent.one
will implicitly execute parent/__init__.py
and parent/one/__init__.py.
Subsequent imports of parent.two
or parent.three
will execute parent/two/__init__.py
and parent/three/__init__.py
respectively.
and under The module cache:
The first place checked during import search is sys.modules
. This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths. So if foo.bar.baz
was previously imported, sys.modules
will contain entries for foo
, foo.bar
, and foo.bar.baz
. Each key will have as its value the corresponding module object.
(bold emphasis in quoted sections added by me).
Note that everything in the urllib3/packages
directory is a vendorized package; a project that normally would be installed independently, but which the urllib3
project has decided to package up with their own distribution to avoid having to worry about what versions to support. six
is such an independent project, you can install it from PyPI.
You can find more information on the six.moves
virtual package in the six
project documentation. It's purpose is to make it easier for library developers to write code that is compatible both with Python 2 and Python 3, without having to worry about what standard library name to import on either version.