Live data from Hacker News

Say “no” to import side‐effects in Python

chrismorgan.info

11–20 of 86 posts

Re: Say “no” to import side‐effects in Python

#11
post #9

Qt and Gtk seem to be the problem here, and they won't change. They aren't python modules as much as they are application frameworks and assume to be in full control. Gtk calls sys.setdefaultencoding("utf-8") when imported, changing the way your strings behave in the whole process.

"Gtk calls sys.setdefaultencoding("utf-8") when imported, changing the way your strings behave in the whole process."

Well, to be fair, that's more an issue with Python than with GTK

Re: Say “no” to import side‐effects in Python

#12

Can anyone suggest best practice alternatives to import side-effects?

Don't run any functions at the root level of your module.

Instead, if you really need long-lasting objects which get initiated once, then use an object, and put any initialisation stuff in it's `__init__` method. Then the module can be imported whenever, but your initialisation stuff is only called when the user of your library creates a new instance of that class.

For bonus points, make your classes able to be used with the `with ...` syntax, so then lifetime is kept to a minimum, and errors/whatever are dealt with by default.

If you really really need to monkey around and take control of the whole python interpreter (gevent, twisted, and possibly some GUI frameworks come to mind...) then don't do that at import time, do it with a `run_forever()` or `take_control` type function.

But yes, a virtualenv for every project does help a lot with not accumulation cruft.

Re: Say “no” to import side‐effects in Python

#13

Can anyone suggest best practice alternatives to import side-effects?

Don't run any functions at the root level of your module. Instead, if you really need long-lasting objects which get initiated once, then use an object, and put any initialisation stuff in it's `__init__` method. Then the module can be imported whenever, but your initialisation stuff is only called when the user of your library creates a new instance of that class. For bonus points, make your classes able to be used…

I sometimes make calls to collections.namedtuple and other class building functions at the same time as the rest of my definitions.

Re: Say “no” to import side‐effects in Python

#14
post #4
post #2

Agreed. Twisted's behaviour of installing a reactor on import has caused me problems which could be worked around by importing conditionally or at a later time, but in cases like this, where one is importing modules dynamically, one doesn't know ahead of time which workarounds one needs.

Twisted's problem is the good old singleton (anti-)pattern. There can only be one reactor ever, and most galling of all, it can never be restarted once stopped.

It should be said that Twisted has put a lot of effort into making it possible to making it possible to deal with multiple reactors (mostly so they can run their test suite including all their supported reactors), and even to make it possible to unit-test Twisted code without a reactor at all (by having standardized mocks for the various things the reactor does).

Of course, that doesn't help the mountains of code written for Twisted that expect a singleton reactor, so we're stuck with it for the foreseeable future. Perhaps in the Brave New World of Python 3, where Twisted is just an implementation detail of the asyncio module, life will be better.

Re: Say “no” to import side‐effects in Python

#15
post #9

Qt and Gtk seem to be the problem here, and they won't change. They aren't python modules as much as they are application frameworks and assume to be in full control. Gtk calls sys.setdefaultencoding("utf-8") when imported, changing the way your strings behave in the whole process.

With regards to Qt, the problem is not Qt but rather that something else is actually trying to use Qt at a time when it shouldn't.

pygtk setting the default encoding to UTF-8 is concerning—if true, that is certainly bad behaviour.

Re: Say “no” to import side‐effects in Python

#16
I do most of my work with Perl, rather than Python, but in Perl there are several kinds of stock import-level side-effects which are actually quite helpful. These are pretty light-weight though. They boil down to:

1. Global lexical changes to Perl. Sometimes this is the whole point of an import (for example Carp::Always, which typically turns any warning or exception string into a full stack dump). I can't imagine doing something like this in Python. However making soemthing like this work properly without breaking too many things requires a heck of a lot of forethought Yes, even Carp::Always may break something.

2. Manipulation of the importing module's symbol table. This is important for lexical extensions to Perl that you don't want to be global (for example Moo, Moose, and PGObject::Util::DBMethod). Among other things this allows MOPs to be added with greater sophistication than the language typically allows. I am not a Python guru but I could imagine metaprogramming side effects to be useful in setting up a consistent and powerful environment.

The problem the author describes is something which is different though, which not only is a side effect issue but also a violation of separation of concerns. There are certain problems you do not want to solve at import time, and connecting with/configuring external components is almost always one of them.

Why? Because integration with external components is almost always something you want the fine-tuning and decision-making to reside with the application developer. That's very different than setting up a consistent lexical programming environment for use (which is what the acceptable side effects do).

Re: Say “no” to import side‐effects in Python

#17

This in no way invalidates the point of the post, but I can't imagine going back to installing everything globally instead of using virtualenv. If you do that, at least you won't have every package you every looked at in your path.

In my case it is to do with having installed system-wide packages—e.g. I installed Frescobaldi, and that puts in that `frescobaldi_app` which eventually makes it crash. For my own development things I do use virtualenv.

But even inside a virtualenv I have had such a problem before.

Re: Say “no” to import side‐effects in Python

#18
post #13

Earlier quoted context omitted.

Don't run any functions at the root level of your module. Instead, if you really need long-lasting objects which get initiated once, then use an object, and put any initialisation stuff in it's `__init__` method. Then the module can be imported whenever, but your initialisation stuff is only called when the user of your library creates a new instance of that class. For bonus points, make your classes able to be used…

I sometimes make calls to collections.namedtuple and other class building functions at the same time as the rest of my definitions.

If you pass verbose=True to namedtuple(), you can see that it's essentially just defining a new class. This is something that people already do at the top level of a module.

Re: Say “no” to import side‐effects in Python

#19
post #10
post #3

Of course you should do this in any language. I once used a Ruby library that, when loaded, would try to connect to a database on a remote machine. Programs which required this library would take several seconds to display their --help output. Because of that and similar incidents, I've learned to import argparse up front but nothing else unless necessary. Once argument parsing is done, then importing other modules b…

> I once used a Ruby library that, when loaded, would try to connect to a database on a remote machine. How is that at all acceptable? I can't believe that a library that phones home would gain any sort of popularity. I can't say I know much of anything about the Ruby community, but if they've conditioned you to jump through hoops like importing modules at specific times to avoid delays, that is a serious problem. Co…

Well, the "home" it was phoning was on the same network, so it wasn't a security issue, just a usability/performance one. This wasn't anything against the Ruby community--just an errant library written by one person who thought it would be more "convenient" if the database connection were established up front.

Re: Say “no” to import side‐effects in Python

#20
post #4

Earlier quoted context omitted.

Twisted's problem is the good old singleton (anti-)pattern. There can only be one reactor ever, and most galling of all, it can never be restarted once stopped.

It should be said that Twisted has put a lot of effort into making it possible to making it possible to deal with multiple reactors (mostly so they can run their test suite including all their supported reactors), and even to make it possible to unit-test Twisted code without a reactor at all (by having standardized mocks for the various things the reactor does). Of course, that doesn't help the mountains of code wri…

Why can't we at least restart a stopped reactor? That seems possible without breaking compatibility.
Post reply on HN