1

I have a main file which is looking at files within a /modules/ folder, it needs to look at every .py file and find all functions that have a specific attribute. An example module will be like this:

def Command1_1():
    True
Command1_1.command = ['cmd1']

def Command1_2():
    True

The code I am currently using to look through each file and function is this:

for module in glob.glob('modules/*.py'):
    print(module)
    tree = ast.parse(open(module, "rt").read(), filename=PyBot.msggrp + module)

    for item in [x.name for x in ast.walk(tree) if isinstance(x, ast.FunctionDef)]:
        if item is not None:
            print(str(item))

Below is what the code produces but I cannot find a way to show if a function has a ".command" attribute:

modules/Placeholder001.py
Command1_1
Command1_2
modules/Placeholder002.py
Command2_1
Command2_2
Command2_3
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Jason
  • 63
  • 6
  • 1
    Any reason why you have to parse the source code and can't just import the files? – Aran-Fey Apr 23 '18 at 12:49
  • @Aran-Fey My goal is to be able to add whatever file I want into the /modules/ folder and have my code reload everything in that folder. Would I be be able to keep the import dynamic and address my issue if I imported the module instead? – Jason Apr 23 '18 at 12:52
  • I'm not sure what "reload" and "dynamic" means in this context, but I don't think manually parsing the code is any more powerful or flexible than importing. You just [import the file from the path](https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path) and then look for functions in its globals. – Aran-Fey Apr 23 '18 at 12:57

1 Answers1

0

The easiest way is to import each file and then look for functions in its global scope. Functions can be identified with the use of callable. Checking if a function has an attribute can be done with hasattr.

The code to import a module from a path is taken from this answer.

from pathlib import Path
import importlib.util

def import_from_path(path):
    spec = importlib.util.spec_from_file_location(path.stem, str(path))
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

for module_path in Path('modules').glob('*.py'):
    module = import_from_path(module_path)

    for name, value in vars(module).items():
        if callable(value):
            has_attribute = hasattr(value, 'command')
            print(name, has_attribute)

Output:

Command1_1 True
Command1_2 False
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149