tags:

views:

53

answers:

3

Using these four files, all in the same directory:

splitter.py

#imagine this is a third-party library
SPLIT_CHAR = ','

class Splitter(object):
    def __init__(self, s, split_char=None):
        self.orig = s
        if not split_char:
            self.splitted = s.split(SPLIT_CHAR)

a.py

#this person makes the mistake of re-setting the global variable
#in splitter to get different behavior, instead of just passing
#in the extra argument
import splitter

splitter.SPLIT_CHAR = '|'

def go():
    s1 = splitter.Splitter("a|b|c|d")
    print s1.orig
    print s1.splitted

b.py

#this person expects the default behavior (splitting commas)
from splitter import Splitter

def go():
    s1 = Splitter('a,b,c,d')
    print s1.orig
    print s1.splitted

experiment.py

import a
import b

a.go() #this one sets the global var in splitter
b.go() #this one expects the default behavior

The output of experiment.py will be:

a|b|c|d
['a', 'b', 'c', 'd'] #okay... everything is fine
a,b,c,d
['a,b,c,d'] #not what the programmer expected.. should be split on commas

Is there a way to prevent this result? In this case, maybe a.py and b.py are written by two different coworkers, yet A's code is affecting B. It can sometimes be useful to overwrite something in a module (e.g. monkeypatching), but in this case it's producing confusing behavior.

Is there some way to make a copy of the module or sandbox the execution so that a.py can't overwrite values in splitter.py and end up affecting b.py?

Also, let's say instead of the simple a.py and b.py, we are running a hundred web apps under mod_python or something (sharing as much as possible to decrease memory), is there a way to prevent one rogue app from tinkering with a module and breaking all the other apps? Something like Google App Engine has this solved, of course :)

+1  A: 

"Is there a way to prevent this result?"

Yes. Find the people who monkeypatched the module and make them stop.

Monkeypatching doesn't require fancy code work-arounds. It requires people to simply cooperate.

If you write a module and some co-worker makes a mess of it, you should talk to that co-worker. It's cheaper, simpler, and more effective in the long run.

S.Lott
Ah the cultural answer :) This makes sense between a few people or coworkers, but in the case of something like Google App Engine, it would be impractical. In the space between those two extremes, I'm wondering if there's a technique that can be used to manage this kind of situation.
lost-theory
In the case of GAE, the monkeypatching person has managed to mess things up -- and -- well -- they've messed things up. This is Python. The source is available. We're all adults. If you mess things up, you're at fault. There's no call for fancy code to prevent people from messing up. Write the simplest thing that works, and leave it at that.
S.Lott
+1  A: 

How about when you instantiate the Splitter, you set it's default split char to whatever it is you want it to be, and make a setter for it, so that people can change it?

Geo
Yes, that's what a.py should have done. I'm talking about in the case where someone (either maliciously or out of ignorance) tries to overwrite something in the module to get a different behavior. Thank you though.
lost-theory
Add a function in the original class that resets the behaviour to the default, each time it's used. I don't know what else to say.
Geo
A: 

Another way to prevent that is to "hint" that splitter.SPLIT_CHAR is "private" by calling it _SPLIT_CHAR. From PEP8, the style guide for Python code:

_single_leading_underscore: weak "internal use" indicator. E.g. "from M import *" does not import objects whose name starts with an underscore.

and

Use one leading underscore only for non-public methods and instance variables

So while neither of those shout "don't mess with me," it is a hint to the next user, at least if they are familiar with Python's style.

Mark Rushakoff