2011-03-31
A slightly unobvious trap with 'from module import *
'
If you are already being lazy, it is easy to
drift into the habit of doing 'from yourmodule import *
' in your
own code, especially in situations where you really are going to use
everything from your module in the code that you're writing (for
example, importing your Django app's models into your view code for
it). As it happens, there is a little trap for you waiting here.
When they write 'from <X> import *
', most people are probably thinking
that it imports everything they've defined in module X into their
current module. But this is not quite what it does. What it actually
does is that it imports everything in module X's namespace into your
namespace. Now, most of what's in module X's namespace is what you
defined in it. But the namespace also includes whatever you imported in
module X; all of those things have now been silently imported into
your namespace too.
(Yes, yes, __all__
can cut this off. I don't think people use
__all__
very much for internal modules and internal imports like
this, unless they are the sort of person who eschews 'from <X> import
*
' in the first place.)
At this point it's quite easy to just start using some of these silently
imported things in your code without realizing that you haven't
explicitly imported them. When dealing with a bunch of directly imported
things (eg, 'from what.util import SomeThing
') and a bunch of your own
code, it's easy to lose track of what you have and haven't imported yet.
When you write the code and it works, you're of course going to assume
that you explicitly imported whatever you're using, because that's the
only way the code could work, right?
Now the problem is that in your code in module Y is quietly depending
on the inner workings of module X. If you revise module X so that it
no longer needs the SomeThing
utility bit and so remove the import,
X will still work but suddenly this other module Y breaks. Oops.
(The extreme case of this is forgetting to import an entire module.
Then you might wind up with all sorts of code in module Y making free
use of, say, 'socket.<whatever>', despite there being nary an 'import
socket
' to be seen. This is the sort of thing that leaves you starting
at the revision history in your version control system and wondering how
the code ever worked in the first place.)
A more perverse version of this has intermediate steps; module X is imported wholesale into module Y, which is in turn imported wholesale into module Z, which is where you accidentally use SomeThing without importing it. After this happens, there's a whole raft of changes that can be done that break module Z.
(I'm sure that your imagination can come up with even more extreme and odd scenarios.)
A really annoying gap in system observability
The other day I had a problem; new gnome-session
processes weren't
working right on one of our major login servers. They didn't quite
hang outright; instead, strace
showed that they seemed to spend all
of their time talking very slowly to one file descriptor instead of
responding to other Gnome processes that were trying to talk to them.
(When this happens, the other Gnome processes are not very happy and your entire Gnome session basically hangs.)
This left me with a big question: what was on the other end of that file descriptor?
Answering this question turned out to be absurdly difficult, and that is the problem. At least theoretically, 'observability' of systems is one of the next big things; everyone is burning with enthusiasm for tools like Solaris's DTrace and Linux's SystemTap. Yet vendors (and Linux people) have almost completely neglected basic observability tools for tasks like simply seeing what processes are connected to.
On Linux, lsof
can be said to be officially supported and it was able
to tell me that the particular file descriptor was a Unix domain socket;
however it couldn't tell me what the other end was connected to, and
I'm not sure that that information is exported by the kernel. On other
Unixes like Solaris, lsof
isn't even officially supported by the
vendor; to the extent that it works (it's often incomplete), it works
only because people have put heroic amounts of effort into reverse
engineering portions of the Solaris kernel and obtaining information by
force and trickery.
Frankly, this is absurd. Tools like lsof
and lslk
have been a vital
part of the sysadmin arsenal for more than fifteen years. Yet it's still
the case that no one (or at least almost no one) officially supports
them and makes sure that they can get the complete information that
sysadmins need, or even makes sure that sysadmins can get the same
information through other tools.
In 2011, in the era of observability as a big thing, it should be trivial for sysadmins to find out information like 'what is this process talking to' or 'what is using resource X', or even 'who is using what resources of type Y'. That it is not says sad things about vendors (and open source developers).
Sidebar: how I answered my question
I used brute force. I hacked my own X environment to strace
my
gnome-session
from the moment it was started; this let me see the
moment when the relevant file descriptor was created and connect()
'd
(it turned out to be talking to the system DBus daemon). However, this
workaround was only possible because I could start the program on demand
and it hung reliably; had I been dealing with a long-running daemon that
was malfunctioning like this, I would have been out of luck.
PS: it turns out that you do not want to restart the system DBus
daemon out from underneath gnome-session
. If you do, all existing
gnome-session
processes immediately exit, taking every user's session
with them.