1

I'm trying to build a dependency tree based on classes that are used inside methods of another class. So not the parent classes. For this i want to check if a method is using any classes that inherits from a special class named Table.

Example:

class TestTableOne(Table):
    """Class for testing table loader"""
    def source(self):
        source_file = os.path.join('data',
                                   'test_table_one.csv')
        return pd.read_csv(source_file, dtype=dtype, converters=converters)

    def output(self):
        output_path = 'out/path'
        return output_path

    def post_processors(self):
        return [
            drop_age_column,
            calculate_new_age
        ]

class TestTableTwo(Table):
    """Class for testing tables loader"""
    def source(self):
        return TestTableOne.fetch()

    def output(self):
        output_path = os.path.join(tempfile.mkdtemp(),
                                   'output',
                                   self.get_cached_filename('test_table_one', 'pkl')
                                  )
        return output_path

    def something_does_nothing(self, table):
        result = TestTableOne.get()
        return result

    def post_processors(self):
        return [
            self.something_does_nothing
        ]

Here i want to be able to check if TestTableTwo.source is dependent on any other classes that inherits from Table, in this case TestTableOne.

So i wan to ask something similar to inspect.classes_that_appears_in(TestTableTwo.source) and get [TestTableOne]

Is this possible in python? Im using python 3 btw.

Ole Henrik Skogstrøm
  • 6,353
  • 10
  • 57
  • 89
  • did you checked [`__mro__`](https://stackoverflow.com/questions/2010692/what-does-mro-do) magic method ? – Chiheb Nexus Jul 31 '17 at 10:23
  • It's possible if you `ast.parse` the source then use the AST to gather the information. There's nothing builtin that'll do it. There may be a 3rd party library if you're lucky though... – Jon Clements Jul 31 '17 at 10:29
  • @ChihebNexus I don't think that makes sense in this case. `TestTableTwo.source.__mro__` does not exist i believe, since that is only to check the method resolution order for class inheritance right? Here I'm trying to see is a class is beeing used inside a method. – Ole Henrik Skogstrøm Jul 31 '17 at 10:30
  • @OleHenrikSkogstrøm what i'm trying to say is to build a list of all the classes that inherits from Table then check the mro of every return of source's method and compare it to the list created in the begenning. – Chiheb Nexus Jul 31 '17 at 10:32
  • @JonClements I'll check out `ast`, thank you :) – Ole Henrik Skogstrøm Jul 31 '17 at 10:32
  • 1
    @ChihebNexus I'm not sure i understand what you mean, could you maybe post an example as an answer? or send me a github gist? – Ole Henrik Skogstrøm Jul 31 '17 at 10:33

2 Answers2

2

Well it's a nasty suggestion but you can give it a try:

With inspect.getsource you can get the source code of a given object, and you can parse the raw text against your needs.

import inspect

class Table(object):
    pass

class TestTableOne(Table):
    """Class for testing table loader"""
    def source(self):
        return None

class TestTableTwo(Table):
    """Class for testing tables loader"""
    def source(self):
        return TestTableOne.source()

print(inspect.getsource(TestTableTwo.source))
print(TestTableOne.__name__ in inspect.getsource(TestTableTwo.source))

This will output:

    def source(self):
        return TestTableOne.source()

True

I'd note that you still need some work to refine this approach to suit your requirements.

Szabolcs
  • 3,990
  • 18
  • 38
  • 1
    Well i'm testing the same module i think i'll answer the question. If you don't matter i can share my solution too. – Chiheb Nexus Jul 31 '17 at 11:06
  • 2
    @ChihebNexus Feel free to do it! – Szabolcs Jul 31 '17 at 11:15
  • @Szabolcs thank you :) I'll test this out and see if it works for me, but do i understand it correctly if this basically just searches the string for `TestTableOne` or something similar? – Ole Henrik Skogstrøm Jul 31 '17 at 11:25
  • @OleHenrikSkogstrøm it's searching for the source code of the called function. – Chiheb Nexus Jul 31 '17 at 11:25
  • 1
    @ChihebNexus if you have another example of how to do this please post an answer :) Would be nice to see another solution to this. – Ole Henrik Skogstrøm Jul 31 '17 at 11:26
  • 1
    @Szabolcs by the way +1 because you've a clear mind and you get the answer quickly. – Chiheb Nexus Jul 31 '17 at 11:33
  • @OleHenrikSkogstrøm yes it's just a naive implementation at the moment and just looks for the name of the class in the source string. So it would also return true if the `'TestTableOne'` substring could be found in any other context. – Szabolcs Jul 31 '17 at 14:33
1

Here is an example using inspect and __mro__:

class Table:
    var = "hello"
    def source(self):
        return self.var

class A(Table):
    def source(self):
        return Table.source()

class B(A):
    def source(self):
        return A.source()

class C(B):
    def source(self):
        return B.source()

def check(cls):
    import inspect
    func_name = inspect.getsourcelines(cls)[0][1].lstrip().strip('\n').split('return')[1].split('.')[0]
    func_name_inherits = [k.__name__ for k in eval(func_name + '.__mro__')]
    if 'Table' in func_name_inherits:
        return True
    else:
        return False

if __name__== '__main__':
    print('The return of C.source() inherits from Table:', check(C.source))
    print('The return of B.source() inherits from Table:', check(B.source))
    print('The return of A.source() inherits from Table:', check(A.source))

Output:

The return of C.source() inherits from Table: True
The return of B.source() inherits from Table: True
The return of A.source() inherits from Table: True
Chiheb Nexus
  • 9,104
  • 4
  • 30
  • 43