== How _os.path_ exposes some Python import weirdness For reasons beyond the scope of this entry, I was recently looking at the source code for the [[os module http://docs.python.org/library/os.html]] when a question suddenly struck me: how on earth does '_import os.path_' work? The conventional way to have submodules like this is to have your module be a directory with an ((__init__.py)) file, and then the submodule is either a Python file or a subdirectory. However, the _os_ module is not a directory; instead it is a single file, _os.py_, with no _os/path.py_ for Python to import in any conventional way. So what's going on? First off, recall that importing a module loads and executes that module's code (or in the case of directory based modules, the code in its ((__init__.py)) file). As it turns out, when you do '_import os.path_' the _os_ module itself is loaded and executed even though it is a plain file and not a subdirectory, and *is also imported into your module*. Once you think about it, it's clear that the _os_ module has to actually be imported into your module here: if it wasn't imported, you would have no way to resolve the name _os.path.isdir_ (for example) to an object, because there would be no '_os_' name binding in your namespace. (I would recommend keeping explicit '_import os_' statements in your code in the name of being more explicit and having less confusion.) What the _os_ module does here is essentially: > _import something as path_ \\ > _sys.modules["os.path"] = path_ Both parts are necessary. Without an entry for "os.path" in ((sys.modules)) after the _os_ module has finished loading, _import_ fails with a 'no module' error. Without a _path_ object in _os_'s namespace, no one could actually look up anything from _os.path_ even though Python would claim that the module had been imported. If you are the right sort of person, you are now wondering what happens if you put the following in your own module, call it _my.py_: > _import something as path_ \\ > _sys.modules["my.path"] = True_ and then start importing _my.path_. The answer is that things get weird because there are two sorts of names and namespaces involved here, the namespace of module names (the keys of ((sys.modules))) and the namespace in your module, and different sorts of _import_ use them differently. Here's how it breaks down in my testing: * '_import my.path_' works because all it cares about is that there is a "my.path" entry in ((sys.modules)) when the dust settles. Actual name lookups go through the _my_ module namespace, where _path_ is a real module. * '_import my.path as path_' works (you get a module instead of _True_), which surprised me; apparently _import_ actually looks up the _path_ name in _my_ instead of taking the object directly from ((sys.modules)). This is somewhat odd, as the _my_ module is *not* imported into your namespace. * '_from my.path import isdir_' fails, which seems odd given the above. I suspect that all of this is subject to change without notice in future CPython versions (although it doesn't seem to have changed even in Python 3.1.1). Still, I find it both interesting and puzzling. (Possibly all of this is documented somewhere in the Python language specification, but I couldn't spot it. The specification for _import_ doesn't seem to talk about this sort of stuff.)