Get methods in module, Python Goal: extract methods/functions defined in module. This excludes:


Imports
Lambda methods
Magic methods
Builtin methods
Class methods
Classes
Non-original definitions (i.e. function assignments, alt_name = orig_fn)


My approach + test below. Any room for improvement, or false positives/negatives? (In particular I wonder if we can get away without typechecks, e.g. isinstance(method, types.LambdaType))



Code: live demo

import utils

def get_module_methods(module):
    def is_module_function(obj):
        return ('&lt;function' in str(obj) and
                module.__name__ in getattr(obj, '__module__', ''))

    def not_lambda(obj):
        return not ('&lt;lambda&gt;' in getattr(obj, '__qualname__', ''))

    def not_magic(obj):
        s = getattr(obj, '__name__', '').split('__')
        return (len(s) &lt; 2) or not (s[0] == s[-1] == '')

    def not_duplicate(name, obj):
        return name == getattr(obj, '__name__', '')

    def cond(name, obj):
        return (is_module_function(obj) and not_lambda(obj) and
                not_magic(obj) and not_duplicate(name, obj))

    objects = {name: getattr(module, name) for name in dir(module)}
    m_methods = {}

    for name, obj in objects.items():
        if cond(name, obj):
            m_methods[name] = obj
    return m_methods

mm = get_module_methods(utils)
_ = [print(k, '--', v) for k, v in mm.items()]




Test:

# utils.py
import random
import numpy as np
from inspect import getsource


def fn1(a, b=5):
  print("wrong animal", a, b)

class Dog():
  meow = fn1
  def __init__(self):
    pass

  def bark(self):
    print("WOOF")

d = Dog()
barker = d.bark
mewoer = d.meow
A = 5
arr = np.random.randn(100, 100)

def __getattr__(name):
  return getattr(random, name, None)

magic = __getattr__
duplicate = fn1
_builtin = str
lambd = lambda: 1


# main.py
import utils

# def get_module_methods(): ...

mm = get_module_methods(utils)
_ = [print(k, '--', v) for k, v in mm.items()]


fn1 -- &lt;function fn1 at 0x7f00a7eb2820&gt;




Edit: additional test case for a lambda surrogate:

import functools

def not_lambda():
    return 1
functools.wraps(lambda i: i)(not_lambda).__name__


Code in question and answer catch not_lambda as a lambda - false positive.
pyclbr

The standard library includes the pyclbr module, which provides functions for building a module browser.  pyclbr.readmodule_ex() returns a tree of nested dicts with the functions (defstatements) and classes (class statements) in a module.  So you just need to check the top level dict for items of class pyclbr.Function.

Note that readmodule_ex takes the name of the module (a str), not the actual module.

Just tested this, and it prints out imported functions and dunder functions as well, so those need to be filtered out.

import pyclbr

module = 'tutils'

for k,v in pyclbr.readmodule_ex(module).items():
    if (    isinstance(v, pyclbr.Function) 
        and v.module==module
        and not v.name.startswith('__')):
        print(f"{v.name} -- {v.file} at {v.lineno}")