I'm trying to figure out how to differentiate between a python module-level function and a method that is not yet bound to an object instance. When the function is an unbound instance method, I need a handle to the class that defined the method. inspect.ismethod()
doesn't work because the method is not yet bound to an object instance. type()
returns 'function' for both a module-level function and an unbound method.
The use case is a plugin framework that uses method and function decorators. I have a generic 'Model' class that can be subclassed to add functionality. Plugin 'actions' are defined in the subclass using the @register_action( name )
decorator. The decorator function is called on import to register the action. The gui then adds menu items for each ACTION in the dictionary -- but the gui should only add an action if the Model class for the gui is an instance of the class where the function/method was registered.
Some sample code is below. Right now my solution is to parse the text of the fcn descriptor, then check if class_str == str( model.__class__ ).split('.')[-1].strip("'>")
. I also tried passing in SpecialModel to the decorator, but SpecialModel is not defined when the decorator function runs so that doesn't work. There must be a better solution for registering class method actions.
Note: since it is not possible to pass SpecialMethod
into the register_action
decorator because SpecialMethod
is not defined, I may need to call get_class_from_function
when it comes time to register an action with the gui.
ACTIONS = dict()
### HACK -- best I could do, but want actual Class, not class_name ###
# FIXME : want to return None, Model, SpecialModel, etc.
def get_class_from_function( fcn ):
qname = fcn.__qualname__.split( '.' )
if len( qname ) > 1: # this is (likely) a class function
cls_name = qname[0]
return cls_name
else:
return None
def register_action( name ):
def register_decorator( fcn ):
global ACTION
cls = get_class_from_function( fcn )
ACTION[ name ] = ( fcn, cls )
return fcn
return register_decorator
@register_action( "Help/About" )
def help_about_function( model ):
pass
class Model( object ):
def __init__( self ):
pass
@register_action( "File/Load" )
def load( self ):
pass
class SpecialModel( Model ):
@register_action( "Special/Custom Action" )
def specialact( self ):
pass