Use a class in the context of a different module Use a class in the context of a different module python-3.x python-3.x

Use a class in the context of a different module


You can't change the globals without affecting all other users of the module, but what you sort of can do is create a private copy of the whole module.

I trust you are familiar with sys.modules, and that if you remove a module from there, Python forgets it was imported, but old objects referencing it will continue to do so. When imported again, a new copy of the module will be made.

A hacky solution to your problem could would be something like this:

import sysimport threading# Remove the original module, but keep it aroundmain_threading = sys.modules.pop('threading')# Get a private copy of the moduleimport threading as private_threading# Cover up evidence by restoring the originalsys.modules['threading'] = main_threading# Modify the private copyprivate_threading._allocate_lock = my_allocate_lock()

And now, private_threading.Lock has globals entirely separate from threading.Lock!

Needless to say, the module wasn't written with this in mind, and especially with a system module such as threading you might run into problems. For example, threading._active is supposed to contain all running threads, but with this solution, neither _active will have them all. The code may also eat your socks and set your house on fire, etc. Test rigorously.


Okay, here's a proof-of-concept that shows how to do it. Note that it only goes one level deep -- properties and nested functions are not adjusted. To implement that, as well as make this more robust, each function's globals() should be compared to the globals() that should be replaced, and only make the substitution if they are the same.

def migrate_class(cls, globals):    """Recreates a class substituting the passed-in globals for the    globals already in the existing class.  This proof-of-concept    version only goes one-level deep (i.e. properties and other nested    functions are not changed)."""    name = cls.__name__    bases = cls.__bases__    new_dict = dict()    if hasattr(cls, '__slots__'):        new_dict['__slots__'] = cls.__slots__        for name in cls.__slots__:            if hasattr(cls, name):                attr = getattr(cls, name)                if callable(attr):                    closure = attr.__closure__                    defaults = attr.__defaults__                    func_code = attr.__code__                    attr = FunctionType(func_code, globals)                new_dict[name] = attr    if hasattr(cls, '__dict__'):        od = getattr(cls, '__dict__')        for name, attr in od.items():            if callable(attr):                closure = attr.__closure__                defaults = attr.__defaults__                kwdefaults = attr.__kwdefaults__                func_code = attr.__code__                attr = FunctionType(func_code, globals, name, defaults, closure)                if kwdefaults:                    attr.__kwdefaults__ = kwdefaults            new_dict[name] = attr    return type(name, bases, new_dict)

After having gone through this excercise, I am really curious as to why you need to do this?


"One cannot change these globals without changing it for all the classes in that module." That's the root of the problem isn't it, and a good explanation of the problem with global variables in general. The use of globals in threading tethers its classes to those global objects.

By the time you jerry-rig something to find and monkey patch each use of a global variable within an individual class from the module, are you any further ahead of just reimplementing the code for your own use?

The only work around that "might" be of use in your situation is something like mock. Mock's patch decorators/context managers (or something similar) could be used to swap out a global variable for the life-time of a given object. It works well within the very controlled context of unit testing, but in any other circumstances I wouldn't recommend it and would think about just reimplementing the code to suit my needs.