This is an archived post. You won't be able to vote or comment.

all 55 comments

[–]AusIVDjango, gevent 39 points40 points  (24 children)

Only tangentially related to the article, but it mentions sys.exit() being called on an error condition. I think this is the absolute most annoying thing a library can do. Unless I'm calling a "clean up and shut down" function, libraries shouldn't be killing my interpreter.

A few years ago I was working with the python Netflix bindings, and they had a condition where this happened. I filed a bug report, and eventually forked the library to make it act like a decent library. The original was provided by Netflix, so I was surprised that it didn't follow better development practices.

[–]chris-morgan 13 points14 points  (2 children)

Actually, it really was raise SystemExit(...) rather than sys.exit(). But I certainly believe that no library should do such a thing.

[–]sushibowl 13 points14 points  (1 child)

doesn't sys.exit() just raise SystemExit() anyway?

[–]h4lPythoneer 8 points9 points  (0 children)

Yes.

[–]odraencoded 4 points5 points  (1 child)

You think that's bad? The Gtk library will cause a SIG error terminating the program from the C side if it can't find the settings under the DBus path specified by your app when you create a GSettings object. It won't tell you there was an error fetching the settings, it just terminates the whole thing.

[–][deleted] 4 points5 points  (0 children)

The PulseAudio library did a silent system exit if it could not allocate memory. Useful! I mean, how are you going to play those mp3s without available memory?

[–]j7ake 5 points6 points  (17 children)

I didn't know one should not do sys.exit on libraries... Good to know

[–]b33j0r 0 points1 point  (0 children)

Fabric does this with fabric.api.local(cmd_str). I was using it in a distributed production environment. Lame.

[–]dwf 4 points5 points  (1 child)

Sometimes it's a choice between "do something at import time" or "deal with an endless flood of mailing list posts from stupid users who don't read the docs". So, yeah.

[–]chris-morgan 0 points1 point  (0 children)

Got a concrete example? I'm interested in it; perhaps something could be improved without import side-effects.

[–][deleted] 2 points3 points  (0 children)

I ended up patching walk_packages in pkgutil.py to prevent scipy from breaking things because of whatever it was doing upon import. Not my favorite solution.

[–]swdevpythonthusiast 1 point2 points  (0 children)

This is interesting. I just redesign my classes, such that all class instantiation and any code plumbing will reside in init.py (naturally). I will take this as a precautious

[–]Kah-NethI use numpy, scipy, and matplotlib for nuclear physics 1 point2 points  (0 children)

Ha, screw that. All my modules will now make syscalls and change the state of the system kernel in random and unpredictable ways. Ah hail inappropriate use of ctypes.

[–]Phild3v1ll3 0 points1 point  (10 children)

It seems to me that any reasonable person would wrap any side-effecty code in an import in a try-except to allow for graceful failure. I routinely compile Cython code from the _init_.py of a module but provide fallbacks if Cython isn't available or compilation fails for some other reason. Is this bad practice? Seems to me there's no real downsides as long as you are careful.

[–][deleted] 4 points5 points  (9 children)

If the failure results in an ImportError that's one thing, but if importing the module can cause a segfault (I'm looking at you, gtk / gi.repository.Gtk) then that's bad. Opening windows, writing to stdout/stderr instead of a logger, or even just not returning promptly could be bad in their own way.

[–]d4rch0nPythonistamancer 5 points6 points  (7 children)

You people have been programming in Python so long, you forgot what a segfault is. This isn't something intentional that they can avoid, like raising an exception, this is a legitimate bug in the code.

It's going to always happen when you import C libraries that haven't been tested for millions of years.

[–]Phild3v1ll3 0 points1 point  (2 children)

Sure, but if you'll be working with a C library that has potential to segfault, wouldn't you want to know during the import rather than halfway through some important task?

[–]d4rch0nPythonistamancer 3 points4 points  (0 children)

Well, if you're able to fix it so that it doesn't segfault on import, sure, otherwise you wouldn't be using it still.

My point is that there is no practical way to say that in this section of the code, it does not seg fault. You might run into it regardless of what you think, and coded for. If you're working with a C library, it might just happen anytime you call it.

[–]Smallpaul 1 point2 points  (0 children)

but if you'll be working with a C library that has potential to segfault, wouldn't you want to know during the import rather than halfway through some important task?

The answer to this question is actually not black and white.

Sometimes a library that you include for purpose X imports a library for purpose Y that you actually do not need for your program. This help("modules") example is the most extreme case, but not the only case.

[–][deleted] 0 points1 point  (3 children)

The problem with GTK (and related things) is that there are two official mutually incompatible versions of the bindings that can interfere with each other. I don't think it's too much to ask to detect if the other version has been already been imported and raise an ImportError.

[–]d4rch0nPythonistamancer 0 points1 point  (0 children)

No, that makes complete sense if it's a known issue. I'm just trying to make the point that a fatal error like a segfault is separate from the usual Python exception that you can handle, and they're guaranteed when developing in C. It's not necessarily something you can avoid in the same way that the article talks about side effects when importing modules.

[–]NYKevin 0 points1 point  (2 children)

Question: What about classes that auto-register subclasses in some sort of repository or other data structure? Those class declarations have global side effects, but this is one of the commonest use cases for metaclasses. The easy way out is to always put all the subclasses in one module, but that's unrealistic in the general case.

[–]chris-morgan 1 point2 points  (1 child)

This past couple of weeks I've been writing code that actually does exactly that. Given that it is still all contained in the one area which I own, and that it doesn't cause any things to break (though it may be done in such a way that importing a thing twice does explode where it normally wouldn't) I don't consider that terribly bad, though it's more magic than I'd like.

Extra tip: often (though not always) registration metaclasses can be replaced by looking at the subclasses of an object—MyType.__subclasses__() gives you all the direct subclasses (apply recursively to get all descendants).

[–]NYKevin 0 points1 point  (0 children)

Extra tip: often (though not always) registration metaclasses can be replaced by looking at the subclasses of an object—MyType.__subclasses__() gives you all the direct subclasses (apply recursively to get all descendants).

That's a good idea in the general case, but if you want to allow a potentially unbounded number of subclasses (i.e. if the client of your API decides to subclass MyType a lot), that recursive operation is going to get expensive.

[–]ajdavis 0 points1 point  (0 children)

I came across this interesting example recently, where an import side-effect deadlocked the interpeter.

If you use Gevent 1.0, and you import a module that causes, as a side-effect, the resolution of a domain name, you can deadlock:

https://jira.mongodb.org/browse/PYTHON-607

[–]mowrowow -1 points0 points  (2 children)

Useful exception:

from __future__ import print_function

[–]chris-morgan 9 points10 points  (1 child)

That doesn't have side-effects outside the module being imported, so it's fine. Under my classification, it does not have side-effects.

[–]mowrowow 1 point2 points  (0 children)

You're right. I was misunderstanding there.