Here's an extreme example of
import
and namespace trickery, drawn from the standard Python
ftplib
module:
try: import SOCKS socket = SOCKS del SOCKS from socket import getfqdn socket.getfqdn = getfqdn del getfqdn except ImportError: import socket
(linebreaks added for clarity)
Modules are objects in Python, so a module's name binding can be manipulated like anything else. This leads to the two things that
ftplib
is doing here.
First, the
ftplib
module would like to use the
SOCKS
module if it exists and otherwise use the standard
socket
module. Rather than sprinkle conditionals throughout its own code, it just imports
SOCKS
and renames it to
socket
, counting on
SOCKS
to have all the socket routines it needs under the same names as
socket
does.
Second,
SOCKS
is apparently missing the
getfqdn
function. So
ftplib
yanks a reference to it out of the real
socket
module and stuffs it into the
SOCKS
module (that
ftplib
knows as '
socket
'). This lets its code can just call
socket.getfqdn
without having to worry about whether
socket
is the real thing or
SOCKS
.
This works because of bonus trickery: unlike other operations, both '
import
' and '
from <blah> import ...
' take the names without looking them up in the current namespace. So even though the name '
socket
' points to the
SOCKS
module at this point, '
from socket
import getfqdn
' goes off to the real
socket
module, instead of trying to find
getfqdn
in the
SOCKS
module.
Because modules are global, the side effect of this is that
ftplib
has changed
SOCKS
's namespace for everyone that's imported it. If somewhere in the program another module has also imported
SOCKS
, they can refer to '
SOCKS.getfqdn
' and it will work.
(There are actually practical uses of altering other modules' namespaces. I may cover some of them in a later entry.)