Reputation: 153
I define a class in a given python module. From a few other python files I will create instances of said class. The instances register themselves at object creation, ie during __init__()
, in a singleton registry object. From a third type of python file I would like to access the registry, look at the objects therein and be able to figure out in which files these objects were created beforehand.
A code sample might look as follows:
Python module file : '/Users/myself/code/myobjectmodule.py':
@singleton
class Registry(object):
def __init__(self):
self.objects = {}
class MyObject(object):
def __init__(self, object_name):
self.object_name = object_name
Registry().objects[self.object_name] = self
singleton
decorator according to http://www.python.org/dev/peps/pep-0318/#examples
Instance creation python files : '/Users/myself/code/instance_creation_python_file.py':
from myobjectmodule import MyObject
A = MyObject('Foo')
Third python file : '/Users/myself/code/registry_access.py':
from myobjectmodule import Registry
registry = Registry()
foo = registry.objects['Foo']
Now, I would like to have a method foo.get_file_of_object_creation()
.
How can I implement this method?
The reason for this approach is the following scenario:
1. A framework defines a set of objects that shall specify data sources and contain the data after loading (MyObject).
2. Apps making use of this framework shall specify these objects and make use of them. Each app is saved in a .py file or a folder that also specifies the name of the app via its name.
3. An engine provides functionality for all apps, but needs to know, for some features, which of the objects originate from which app / file.
Upvotes: 15
Views: 4175
Reputation: 63282
This answer is slightly different in that it does not use inspect.stack
, considering I observed it to be particularly slow in Python 3.
import inspect
class Locatable:
def __new__(cls, *_args, **_kwargs):
# Background: http://eli.thegreenplace.net/2012/04/16/python-object-creation-sequence
obj = super().__new__(cls)
obj.location = obj._initialization_location() # pylint: disable=protected-access
return obj
@staticmethod
def _initialization_location():
# Background: https://stackoverflow.com/a/42653524/
frame = inspect.currentframe()
while frame:
if frame.f_code.co_name == '<module>':
return {'module': frame.f_globals['__name__'], 'line': frame.f_lineno}
frame = frame.f_back
@property
def name(self):
module_name = self.__module__
class_name = self.__class__.__qualname__ # pylint: disable=no-member
return module_name + '.' + class_name
The above is a base class that can be inherited from.
The location
attribute should contain the name of the module where the class is instantiated, e.g. mypackage.mymodule
.
Upvotes: 3
Reputation: 6661
Without getting into the merits of why would you want to do this, here is a way to do it:
# assume the file is saved as "temp.py"
import inspect
class RegisteredObject(object):
def __new__(cls, *args, **kwargs):
new_instance = super(RegisteredObject, cls).__new__(cls, *args, **kwargs)
stack_trace = inspect.stack()
created_at = '%s:%d' % (
stack_trace[1][1], stack_trace[1][2])
new_instance.created_at = created_at
return new_instance
def get_file_of_object_creation(self):
return self.created_at
class MyObject(RegisteredObject):
pass
def create_A():
return MyObject()
def create_B():
return MyObject()
if __name__ == '__main__':
t1 = create_A()
t2 = create_B()
t3 = create_A()
t4 = create_B()
t5 = MyObject()
print '"t1" was created at "%s"' % t1.get_file_of_object_creation()
print '"t2" was created at "%s"' % t2.get_file_of_object_creation()
print '"t3" was created at "%s"' % t3.get_file_of_object_creation()
print '"t4" was created at "%s"' % t4.get_file_of_object_creation()
print '"t5" was created at "%s"' % t5.get_file_of_object_creation()
Output:
$ python temp.py
"t1" was created at "temp.py:19"
"t2" was created at "temp.py:22"
"t3" was created at "temp.py:19"
"t4" was created at "temp.py:22"
"t5" was created at "temp.py:29"
Upvotes: 12
Reputation: 363547
All the caveats about this only being a good idea for debugging aside, you can use the inspect
module.
import inspect
def get_caller():
return inspect.stack()[2] # 1 is get_caller's caller
def trace_call():
_, filename, line, function, _, _ = get_caller()
print("Called by %r at %r:%d" % (function, filename, line))
def main():
trace_call()
main()
produces
Called by 'main' at 'trace.py':11
Upvotes: 9