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.