4

I was wandering how does elixir\sqlalchemy get to know all the entity classes I've declared in my model, when I call setup_all()? I need that kind of functionality in a little project of mine, but I have no clue. I've tried to steptrace through elixir's setup_all(), and I found that it keeps a collection of all entity classes in a "global" list (or was it dict?), but I can't catch the moment when the list is filled. Any ideas?

Ben
  • 51,770
  • 36
  • 127
  • 149
AlexVhr
  • 2,014
  • 1
  • 20
  • 30

2 Answers2

10

For class definitions, this is easier (no importing)

def find_subclasses(cls):
    results = []
    for sc in cls.__subclasses__():
        results.append(sc)
    return results

I'm not sure if you wanted this, or objects. If you want objects:

import gc

def find_subclasses(cls):
    results = []
    for sc in cls.__subclasses__():
        for obj in gc.get_objects():
            if isinstance(obj, sc):
               results.append(obj)
    return results
elijaheac
  • 912
  • 2
  • 8
  • 23
  • Yes, I want definitions, not instances. Thank you! Short version rules. – AlexVhr Jan 23 '12 at 12:03
  • Howewer, it should be mentioned that __subclasses__ is available for new style classes only, and it's a callable, so it should be `cls.__subclasses__()` at least on 2.7.x – AlexVhr Jan 23 '12 at 14:49
  • 3
    @AlexVhr You really shouldn't be using old-style classes anymore though. – elijaheac Jan 24 '12 at 01:10
4

Answering the main question, without dealign with SQLALchemy or elixir at all - yes, it is possible in Python.

The garbage colector (gc) module on the standard library, have a function call that allows one to retrieve all references to a given object, interpreter wide. A class is always referred to in the __mro__ attribute of any inherited classes.

So, the following function could retrieve all classes that inherit from a given class:

import gc
def find_subclasses(cls):
    all_refs = gc.get_referrers(cls)
    results = []
    for obj in all_refs:
        # __mro__ attributes are tuples
        # and if a tuple is found here, the given class is one of its members
        if (isinstance(obj, tuple) and
            # check if the found tuple is the __mro__ attribute of a class
            getattr(obj[0], "__mro__", None) is obj):
            results.append(obj[0])
    return results
jsbueno
  • 99,910
  • 10
  • 151
  • 209