0

Is there a way to get a reference to the local variables defined in a different module?

for example, I have two files: framework.py and user_code.py:

framework.py:

from kivy.app import App

class BASE_A:
    pass

class MyApp(App):
    def on_start(self):
        '''Here I'd like to get a reference to sub-classes of BASE_A and
        instantiated objects of these sub-classes, defined in the file
        "user_code.py" such as a1, a2, as well as the class A itself,
        without explicitly passing them to MyApp's instance.
        '''

user_code.py:

from framework import MyApp

class A(BASE_A):
    pass

app = MyApp()
a1 = A()
a2 = A()

app.run()

What I'd like to do is to somehow get a reference to the objects a1 and a2, as well as the class A, that were all defined in user_code.py. I'd like to use them in the method on_start, which is invoked in app.run().

Is it possible, for example, to get a reference to the scope in which the MyApp object was defined (user_code.py)?

Some background for anyone who's interested:

I know it's a bit of an odd question, but the reason is:

I'm writing a python framework for creating custom-made GUI control programs for self-made instruments, based on Arduino. It's called Instrumentino (sitting in GitHub) and I'm currently developing version 2.

For people to use the framework, they need to define a system description file (user_code.py in the example) where they declare what parts they're using in their system (python objects), as well as what type of actions the system should perform (python classes).

What I'm trying to achieve is to automatically identify these objects and classes in MyApp's on_start without asking the user to explicitly pass these objects and classes, in order to make the user code cleaner. Meaning to avoid code such as:

app.add_object(a1)
app.add_object(a2)
app.add_class(A)
yoel
  • 305
  • 4
  • 17
  • Why do you want to do this? What problem would it solve? – Peter Wood Feb 19 '16 at 22:47
  • 1
    You _can_ get it via `globals` -- But you probably don't actually want to do that. In this paradigm, the `A` class would be completely useless if the user imports it (e.g. it isn't executed in the `__main__` module) since `b` wouldn't be defined in that case. – mgilson Feb 19 '16 at 22:49
  • At least in your code, `b` doesn't exist yet when `A.__init__` gets called for the first time. – chepner Feb 19 '16 at 22:50
  • Sorry guys, I shouldn't have posted so late in the night. I've edited the question to be better defined and added some background info. – yoel Feb 20 '16 at 10:40

2 Answers2

1

New-style classes in Python have a method named __subclasses__ which returns a list of all direct subclasses that have been defined so far. You can use that to get a hold of the A class in your example, just call BASE_A.__subclasses__() (if you're using Python 2, you'll also need to change BASE_A to inherit from object). See this question and its answers for more details (especially the functions to recursively get all subclasses).

As for getting access to the instances, for that you probably should add some code to the base class, perhaps saving the instances created by __new__ into some kind of data structure (e.g. a weakset). See this question and its answers for more on that part. Actually, now that I think about it, if you put your instances into a centralized data structure somewhere (e.g. not in an attribute of each subclass), you might not need the function to search for the classes, since you can just inspect the type of the instances and find the subclasses that are being used.

Community
  • 1
  • 1
Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • Thanks! I'm still missing something thought. It might happen that I want to load several user_code files, so I'd need a way to distinguish between classes defined in user_code_1.py and user_code_2.py (same for object instances). I could wrap it all up in a class, but I'd like to avoid that and somehow screen the user_code file for relevant instances and classes. Would it help if I gave MyApp's __init__ a path to the user_code.py file? Can I then scan it somehow? – yoel Feb 20 '16 at 21:01
  • Hmm, that's rather more difficult, you might need to do introspection after all. Can you rely on each user module creating its own subclass and instances of that class? That might be easiest. Otherwise, you might want to use the `__subclasses__` approach, then grab the `__module__` attribute from the class and call `__import__` on the module name to get the class. Then you can inspect `user_module.__dict__`. I don't think there's any simple way to get at the location where the instances are being stored directly (you could try inspecting the stack frames, but that's going to be awkward). – Blckknght Feb 20 '16 at 21:10
  • Thanks, that seems like the way to go. But I'm still missing how I can automatically get the user_code module names inside MyApp. Is there a way to get the name of the module in which an instance is created? I mean, if I do "app = MyApp()" in user_code.py, is it possible to get the path for user_code.py inside the __init__ method of app's instance? – yoel Feb 22 '16 at 20:20
  • Got it! I'm simply searching in sys.modules[__main__].__dict__.values() for the right objects and class definitions. Thanks! – yoel Feb 22 '16 at 21:02
0

Your question is a bit illogical.

Since Python interprets the code sequentially: b is not defined before a initialization.

If you can set b before a then:

b = None # global variable

class A():
    global b
    def __init__(self):
        '''Here I'd like to get a reference to b (of type B) without passing it as an argument'''

class B_BASE():
    pass

class B(B_BASE):
    def __init__(self):
        pass

if __name__ == '__main__':
    b = B()
    a = A()

I wouldn't recommend doing this because I find that this isn't clean. Since you have a dependency on b in a you should pass it as a parameter to the A class

Yves Lange
  • 3,914
  • 3
  • 21
  • 33